Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfreelist_p.h
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QFREELIST_P_H
6#define QFREELIST_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QtCore/private/qglobal_p.h>
20
21#include <QtCore/q20atomic.h>
22
23QT_BEGIN_NAMESPACE
25/*! \internal
26
27 Element in a QFreeList. ConstReferenceType and ReferenceType are used as
28 the return values for QFreeList::at() and QFreeList::operator[](). Contains
29 the real data storage (_t) and the id of the next free element (next).
30
31 Note: the t() functions should be used to access the data, not _t.
32*/
33template <typename T>
34struct QFreeListElement
35{
36 typedef const T &ConstReferenceType;
37 typedef T &ReferenceType;
38
39 T _t;
40 q20::atomic<int> next;
41
42 inline ConstReferenceType t() const { return _t; }
43 inline ReferenceType t() { return _t; }
44};
45
46/*! \internal
47
48 Element in a QFreeList without a payload. ConstReferenceType and
49 ReferenceType are void, the t() functions return void and are empty.
50*/
51template <>
52struct QFreeListElement<void>
53{
54 typedef void ConstReferenceType;
55 typedef void ReferenceType;
56
58
59 inline void t() const { }
60 inline void t() { }
61};
62
63/*! \internal
64
65 Defines default constants used by QFreeList:
66
67 - The initial value returned by QFreeList::next() is zero.
68
69 - QFreeList allows for up to 16777216 elements in QFreeList and uses the top
70 8 bits to store a serial counter for ABA protection.
71
72 - QFreeList will make a maximum of 4 allocations (blocks), with each
73 successive block larger than the previous.
74
75 - Sizes static int[] array to define the size of each block.
76
77 It is possible to define your own constants struct/class and give this to
78 QFreeList to customize/tune the behavior.
79*/
80struct Q_AUTOTEST_EXPORT QFreeListDefaultConstants
81{
82 // used by QFreeList, make sure to define all of when customizing
83 enum {
84 InitialNextValue = 0,
85 IndexMask = 0x00ffffff,
86 SerialMask = ~IndexMask & ~0x80000000,
87 SerialCounter = IndexMask + 1,
88 MaxIndex = IndexMask,
89 BlockCount = 4
90 };
91
92 static const int Sizes[BlockCount];
93};
94
95/*! \internal
96
97 This is a generic implementation of a lock-free free list. Use next() to
98 get the next free entry in the list, and release(id) when done with the id.
99
100 This version is templated and allows having a payload of type T which can
101 be accessed using the id returned by next(). The payload is allocated and
102 deallocated automatically by the free list, but *NOT* when calling
103 next()/release(). Initialization should be done by code needing it after
104 next() returns. Likewise, cleanup() should happen before calling release().
105 It is possible to have use 'void' as the payload type, in which case the
106 free list only contains indexes to the next free entry.
107
108 The ConstantsType type defaults to QFreeListDefaultConstants above. You can
109 define your custom ConstantsType, see above for details on what needs to be
110 available.
111*/
112template <typename T, typename ConstantsType = QFreeListDefaultConstants>
114{
115 typedef T ValueType;
117 typedef typename ElementType::ConstReferenceType ConstReferenceType;
118 typedef typename ElementType::ReferenceType ReferenceType;
119
120 // return which block the index \a x falls in, and modify \a x to be the index into that block
121 static inline int blockfor(int &x)
122 {
123 x = x & ConstantsType::IndexMask;
124 for (int i = 0; i < ConstantsType::BlockCount; ++i) {
125 int size = ConstantsType::Sizes[i];
126 if (x < size)
127 return i;
128 x -= size;
129 }
130 Q_UNREACHABLE_RETURN(-1);
131 }
132
133 // allocate a block of the given \a size, initialized starting with the given \a offset
134 static inline ElementType *allocate(int offset, int size)
135 {
136 // qDebug("QFreeList: allocating %d elements (%ld bytes) with offset %d", size, size * sizeof(ElementType), offset);
137 ElementType *v = new ElementType[size];
138 for (int i = 0; i < size; ++i)
139 v[i].next.store(offset + i + 1, std::memory_order_relaxed);
140 return v;
141 }
142
143 // take the current serial number from \a o, increment it, and store it in \a n
144 static inline int incrementserial(int o, int n)
145 {
146 return int((uint(n) & ConstantsType::IndexMask) | ((uint(o) + ConstantsType::SerialCounter) & ConstantsType::SerialMask));
147 }
148
149 // the blocks
150 q20::atomic<ElementType *> _v[ConstantsType::BlockCount];
151 // the next free id
152 q20::atomic<int> _next;
153
154 // QFreeList is not copyable
156
157public:
158 constexpr inline QFreeList();
159 inline ~QFreeList();
160
161 // returns the payload for the given index \a x
162 inline ConstReferenceType at(int x) const;
163 inline ReferenceType operator[](int x);
164
165 /*
166 Return the next free id. Use this id to access the payload (see above).
167 Call release(id) when done using the id.
168 */
169 inline int next();
170 inline void release(int id);
171};
172
173template <typename T, typename ConstantsType>
174constexpr inline QFreeList<T, ConstantsType>::QFreeList()
175 :
176 _v{}, // uniform initialization required
177 _next(ConstantsType::InitialNextValue)
178{ }
179
180template <typename T, typename ConstantsType>
181inline QFreeList<T, ConstantsType>::~QFreeList()
182{
183 for (int i = 0; i < ConstantsType::BlockCount; ++i)
184 delete [] _v[i].load(std::memory_order_acquire);
185}
186
187template <typename T, typename ConstantsType>
188inline typename QFreeList<T, ConstantsType>::ConstReferenceType QFreeList<T, ConstantsType>::at(int x) const
189{
190 const int block = blockfor(x);
191 return (_v[block].load(std::memory_order_relaxed))[x].t();
192}
193
194template <typename T, typename ConstantsType>
195inline typename QFreeList<T, ConstantsType>::ReferenceType QFreeList<T, ConstantsType>::operator[](int x)
196{
197 const int block = blockfor(x);
198 return (_v[block].load(std::memory_order_relaxed))[x].t();
199}
200
201template <typename T, typename ConstantsType>
202inline int QFreeList<T, ConstantsType>::next()
203{
204 int newid;
205 int id = _next.load(std::memory_order_acquire);
206 do {
207 int at = id & ConstantsType::IndexMask;
208 const int block = blockfor(at);
209 ElementType *v = _v[block].load(std::memory_order_acquire);
210
211 if (!v) {
212 ElementType* const alloced = allocate((id & ConstantsType::IndexMask) - at,
213 ConstantsType::Sizes[block]);
214 if (_v[block].compare_exchange_strong(v, alloced, std::memory_order_release, std::memory_order_acquire)) {
215 v = alloced;
216 } else {
217 // race with another thread lost
218 delete[] alloced;
219 Q_ASSERT(v != nullptr);
220 }
221 }
222
223 newid = v[at].next.load(std::memory_order_relaxed) | (id & ~ConstantsType::IndexMask);
224 } while (!_next.compare_exchange_strong(id, newid, std::memory_order_release, std::memory_order_acquire));
225 // qDebug("QFreeList::next(): returning %d (_next now %d, serial %d)",
226 // id & ConstantsType::IndexMask,
227 // newid & ConstantsType::IndexMask,
228 // (newid & ~ConstantsType::IndexMask) >> 24);
229 return id;
230}
231
232template <typename T, typename ConstantsType>
233inline void QFreeList<T, ConstantsType>::release(int id)
234{
235 int at = id & ConstantsType::IndexMask;
236 const int block = blockfor(at);
237 ElementType *v = _v[block].load(std::memory_order_acquire);
238
239 int x, newid;
240 do {
241 x = _next.load(std::memory_order_acquire);
242 v[at].next.store(x & ConstantsType::IndexMask, std::memory_order_relaxed);
243
244 newid = incrementserial(x, id);
245 } while (!_next.compare_exchange_weak(x, newid, std::memory_order_release));
246 // qDebug("QFreeList::release(%d): _next now %d (was %d), serial %d",
247 // id & ConstantsType::IndexMask,
248 // newid & ConstantsType::IndexMask,
249 // x & ConstantsType::IndexMask,
250 // (newid & ~ConstantsType::IndexMask) >> 24);
251}
252
253QT_END_NAMESPACE
254
255#endif // QFREELIST_P_H
ReferenceType operator[](int x)
ConstReferenceType at(int x) const
void release(int id)
Combined button and popup list for selecting options.
q20::atomic< int > next
Definition qfreelist_p.h:57