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
qthreadstorage.cpp
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
7
8#include "private/qcoreapplication_p.h"
9#include "qthread.h"
10#include "qthread_p.h"
11#include "qmutex.h"
12
13#include <string.h>
14
15QT_BEGIN_NAMESPACE
16
17// #define THREADSTORAGE_DEBUG
18#ifdef THREADSTORAGE_DEBUG
19# define DEBUG_MSG qtsDebug
20
21# include <stdio.h>
22# include <stdarg.h>
23void qtsDebug(const char *fmt, ...)
24{
25 va_list va;
26 va_start(va, fmt);
27
28 fprintf(stderr, "QThreadStorage: ");
29 vfprintf(stderr, fmt, va);
30 fprintf(stderr, "\n");
31
32 va_end(va);
33}
34#else
35# define DEBUG_MSG if (false)qDebug
36#endif
37
38Q_CONSTINIT static QBasicMutex destructorsMutex;
39typedef QList<void (*)(void *)> DestructorMap;
40Q_GLOBAL_STATIC(DestructorMap, destructors)
41
42QThreadStorageData::QThreadStorageData(void (*func)(void *))
43{
44 QMutexLocker locker(&destructorsMutex);
45 DestructorMap *destr = destructors();
46 if (!destr) {
47 /*
48 the destructors vector has already been destroyed, yet a new
49 QThreadStorage is being allocated. this can only happen during global
50 destruction, at which point we assume that there is only one thread.
51 in order to keep QThreadStorage working, we need somewhere to store
52 the data, best place we have in this situation is at the tail of the
53 current thread's tls vector. the destructor is ignored, since we have
54 no where to store it, and no way to actually call it.
55 */
56 QThreadData *data = QThreadData::current();
57 id = data->tls.size();
58 DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p cannot be stored", id, func);
59 return;
60 }
61 for (id = 0; id < destr->size(); id++) {
62 if (destr->at(id) == nullptr)
63 break;
64 }
65 if (id == destr->size()) {
66 destr->append(func);
67 } else {
68 (*destr)[id] = func;
69 }
70 DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p", id, func);
71}
72
73QThreadStorageData::~QThreadStorageData()
74{
75 DEBUG_MSG("QThreadStorageData: Released id %d", id);
76 QMutexLocker locker(&destructorsMutex);
77 if (destructors())
78 (*destructors())[id] = nullptr;
79}
80
81void **QThreadStorageData::get() const
82{
83 QThreadData *data = QThreadData::current();
84 if (!data) {
85 qWarning("QThreadStorage::get: QThreadStorage can only be used with threads started with QThread");
86 return nullptr;
87 }
88 QList<void *> &tls = data->tls;
89 if (tls.size() <= id)
90 tls.resize(id + 1);
91 void **v = &tls[id];
92
93 DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p",
94 id,
95 *v,
96 data->thread.loadRelaxed());
97
98 return *v ? v : nullptr;
99}
100
101void **QThreadStorageData::set(void *p)
102{
103 QThreadData *data = QThreadData::current();
104 if (!data) {
105 qWarning("QThreadStorage::set: QThreadStorage can only be used with threads started with QThread");
106 return nullptr;
107 }
108 QList<void *> &tls = data->tls;
109 if (tls.size() <= id)
110 tls.resize(id + 1);
111
112 void *&value = tls[id];
113 // delete any previous data
114 if (value != nullptr) {
115 DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p",
116 id,
117 value,
118 data->thread.loadRelaxed());
119
120 QMutexLocker locker(&destructorsMutex);
121 DestructorMap *destr = destructors();
122 void (*destructor)(void *) = destr ? destr->value(id) : nullptr;
123 locker.unlock();
124
125 void *q = value;
126 value = nullptr;
127
128 if (destructor)
129 destructor(q);
130 }
131
132 // store new data
133 value = p;
134 DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread.loadRelaxed(), p);
135 return &value;
136}
137
138void QThreadStoragePrivate::init()
139{
140 // Make sure the Q_GLOBAL_STATIC is initialized, ensuring consistent
141 // destruction order.
142 destructors();
143}
144
145void QThreadStoragePrivate::finish(QList<void *> *tls)
146{
147 if (tls->isEmpty() || !destructors())
148 return; // nothing to do
149
150 DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread());
151 while (!tls->isEmpty()) {
152 void *&value = tls->last();
153 void *q = value;
154 value = nullptr;
155 int i = tls->size() - 1;
156 tls->resize(i);
157
158 if (!q) {
159 // data already deleted
160 continue;
161 }
162
163 QMutexLocker locker(&destructorsMutex);
164 void (*destructor)(void *) = destructors()->value(i);
165 locker.unlock();
166
167 if (!destructor) {
168 if (QCoreApplication::instanceExists())
169 qWarning("QThreadStorage: entry %d destroyed before end of thread %p",
170 i, QThread::currentThread());
171 continue;
172 }
173 destructor(q); //crash here might mean the thread exited after qthreadstorage was destroyed
174
175 if (tls->size() > i) {
176 //re reset the tls in case it has been recreated by its own destructor.
177 (*tls)[i] = nullptr;
178 }
179 }
180 tls->clear();
181}
182
183/*!
184 \class QThreadStorage
185 \inmodule QtCore
186 \brief The QThreadStorage class provides per-thread data storage.
187
188 \threadsafe
189
190 \ingroup thread
191
192 QThreadStorage is a template class that provides per-thread data
193 storage.
194
195 The setLocalData() function stores a single thread-specific value
196 for the calling thread. The data can be accessed later using
197 localData().
198
199 The hasLocalData() function allows the programmer to determine if
200 data has previously been set using the setLocalData() function.
201 This is also useful for lazy initialization.
202
203 If T is a pointer type, QThreadStorage takes ownership of the data
204 (which must be created on the heap with \c new) and deletes it when
205 the thread exits, either normally or via termination.
206
207 For example, the following code uses QThreadStorage to store a
208 single cache for each thread that calls the cacheObject() and
209 removeFromCache() functions. The cache is automatically
210 deleted when the calling thread exits.
211
212 \snippet threads/threads.cpp 7
213 \snippet threads/threads.cpp 8
214 \snippet threads/threads.cpp 9
215
216 \section1 Caveats
217
218 \list
219
220 \li The QThreadStorage destructor does not delete per-thread data.
221 QThreadStorage only deletes per-thread data when the thread exits
222 or when setLocalData() is called multiple times.
223
224 \li QThreadStorage can be used to store data for the \c main()
225 thread. QThreadStorage deletes all data set for the \c main()
226 thread when QApplication is destroyed, regardless of whether or
227 not the \c main() thread has actually finished.
228
229 \endlist
230
231 \sa QThread
232*/
233
234/*!
235 \fn template <class T> QThreadStorage<T>::QThreadStorage()
236
237 Constructs a new per-thread data storage object.
238*/
239
240/*!
241 \fn template <class T> QThreadStorage<T>::~QThreadStorage()
242
243 Destroys the per-thread data storage object.
244
245 Note: The per-thread data stored is not deleted. Any data left
246 in QThreadStorage is leaked. Make sure that all threads using
247 QThreadStorage have exited before deleting the QThreadStorage.
248
249 \sa hasLocalData()
250*/
251
252/*!
253 \fn template <class T> bool QThreadStorage<T>::hasLocalData() const
254
255 If T is a pointer type, returns \c true if the calling thread has
256 non-zero data available.
257
258 If T is a value type, returns whether the data has already been
259 constructed by calling setLocalData or localData.
260
261 \sa localData()
262*/
263
264/*!
265 \fn template <class T> T &QThreadStorage<T>::localData()
266
267 Returns a reference to the data that was set by the calling
268 thread.
269
270 If no data has been set, this will create a default constructed
271 instance of type T.
272
273 \sa hasLocalData()
274*/
275
276/*!
277 \fn template <class T> const T QThreadStorage<T>::localData() const
278 \overload
279
280 Returns a copy of the data that was set by the calling thread.
281
282 \sa hasLocalData()
283*/
284
285/*!
286 \fn template <class T> void QThreadStorage<T>::setLocalData(T data)
287
288 Sets the local data for the calling thread to \a data. It can be
289 accessed later using the localData() functions.
290
291 If T is a pointer type, QThreadStorage takes ownership of the data
292 and deletes it automatically either when the thread exits (either
293 normally or via termination) or when setLocalData() is called again.
294
295 \sa localData(), hasLocalData()
296*/
297
298QT_END_NAMESPACE
Definition qlist.h:80
QMutex QBasicMutex
Definition qmutex.h:360
static Q_CONSTINIT QBasicMutex destructorsMutex
#define DEBUG_MSG
QList< void(*)(void *)> DestructorMap