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