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