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 QList<void *> &tls = data->tls;
85 if (tls.size() <= id)
86 tls.resize(id + 1);
87 void **v = &tls[id];
88
89 DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p",
90 id,
91 *v,
92 data->thread.loadRelaxed());
93
94 return *v ? v : nullptr;
95}
96
97void **QThreadStorageData::set(void *p)
98{
99 QThreadData *data = QThreadData::current();
100 QList<void *> &tls = data->tls;
101 if (tls.size() <= id)
102 tls.resize(id + 1);
103
104 void *&value = tls[id];
105 // delete any previous data
106 if (value != nullptr) {
107 DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p",
108 id,
109 value,
110 data->thread.loadRelaxed());
111
112 QMutexLocker locker(&destructorsMutex);
113 DestructorMap *destr = destructors();
114 void (*destructor)(void *) = destr ? destr->value(id) : nullptr;
115 locker.unlock();
116
117 void *q = value;
118 value = nullptr;
119
120 if (destructor)
121 destructor(q);
122 }
123
124 // store new data
125 value = p;
126 DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread.loadRelaxed(), p);
127 return &value;
128}
129
130void QThreadStoragePrivate::init()
131{
132 // Make sure the Q_GLOBAL_STATIC is initialized, ensuring consistent
133 // destruction order.
134 destructors();
135}
136
137void QThreadStoragePrivate::finish(QList<void *> *tls, bool suppressWarnings)
138{
139 if (tls->isEmpty() || !destructors())
140 return; // nothing to do
141 if (!QCoreApplication::instanceExists())
142 suppressWarnings = true;
143
144 DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread());
145 while (!tls->isEmpty()) {
146 void *&value = tls->last();
147 void *q = value;
148 value = nullptr;
149 int i = tls->size() - 1;
150 tls->resize(i);
151
152 if (!q) {
153 // data already deleted
154 continue;
155 }
156
157 QMutexLocker locker(&destructorsMutex);
158 void (*destructor)(void *) = destructors()->value(i);
159 locker.unlock();
160
161 if (!destructor) {
162 if (!suppressWarnings)
163 qWarning("QThreadStorage: entry %d destroyed before end of thread %p",
164 i, QThread::currentThread());
165 continue;
166 }
167 destructor(q); //crash here might mean the thread exited after qthreadstorage was destroyed
168
169 if (tls->size() > i) {
170 //re reset the tls in case it has been recreated by its own destructor.
171 (*tls)[i] = nullptr;
172 }
173 }
174 tls->clear();
175}
176
177/*!
178 \class QThreadStorage
179 \inmodule QtCore
180 \brief The QThreadStorage class provides per-thread data storage.
181
182 \threadsafe
183
184 \ingroup thread
185
186 QThreadStorage is a template class where the template parameter \a T
187 specifies the type of data stored per-thread.
188
189 The setLocalData() function stores a single thread-specific value
190 for the calling thread. The data can be accessed later using
191 localData().
192
193 The hasLocalData() function allows the programmer to determine if
194 data has previously been set using the setLocalData() function.
195 This is also useful for lazy initialization.
196
197 If T is a pointer type, QThreadStorage takes ownership of the data
198 (which must be created on the heap with \c new) and deletes it when
199 the thread exits, either normally or via termination.
200
201 For example, the following code uses QThreadStorage to store a
202 single cache for each thread that calls the cacheObject() and
203 removeFromCache() functions. The cache is automatically
204 deleted when the calling thread exits.
205
206 \snippet threads/threads.cpp 7
207 \snippet threads/threads.cpp 8
208 \snippet threads/threads.cpp 9
209
210 \section1 Caveats
211
212 \list
213
214 \li The QThreadStorage destructor does not delete per-thread data.
215 QThreadStorage only deletes per-thread data when the thread exits
216 or when setLocalData() is called multiple times.
217
218 \li QThreadStorage can be used to store data for the \c main()
219 thread. QThreadStorage deletes all data set for the \c main()
220 thread when QApplication is destroyed, regardless of whether or
221 not the \c main() thread has actually finished.
222
223 \endlist
224
225 \sa QThread
226*/
227
228/*!
229 \fn template <class T> QThreadStorage<T>::QThreadStorage()
230
231 Constructs a new per-thread data storage object.
232*/
233
234/*!
235 \fn template <class T> QThreadStorage<T>::~QThreadStorage()
236
237 Destroys the per-thread data storage object.
238
239 Note: The per-thread data stored is not deleted. Any data left
240 in QThreadStorage is leaked. Make sure that all threads using
241 QThreadStorage have exited before deleting the QThreadStorage.
242
243 \sa hasLocalData()
244*/
245
246/*!
247 \fn template <class T> bool QThreadStorage<T>::hasLocalData() const
248
249 If T is a pointer type, returns \c true if the calling thread has
250 non-zero data available.
251
252 If T is a value type, returns whether the data has already been
253 constructed by calling setLocalData or localData.
254
255 \sa localData()
256*/
257
258/*!
259 \fn template <class T> T &QThreadStorage<T>::localData()
260
261 Returns a reference to the data that was set by the calling
262 thread.
263
264 If no data has been set, this will create a default constructed
265 instance of type T.
266
267 \sa hasLocalData()
268*/
269
270/*!
271 \fn template <class T> const T QThreadStorage<T>::localData() const
272 \overload
273
274 Returns a copy of the data that was set by the calling thread.
275
276 \sa hasLocalData()
277*/
278
279/*!
280 \fn template <class T> void QThreadStorage<T>::setLocalData(T data)
281
282 Sets the local data for the calling thread to \a data. It can be
283 accessed later using the localData() functions.
284
285 If T is a pointer type, QThreadStorage takes ownership of the data
286 and deletes it automatically either when the thread exits (either
287 normally or via termination) or when setLocalData() is called again.
288
289 \sa localData(), hasLocalData()
290*/
291
292QT_END_NAMESPACE
Definition qlist.h:82
QMutex QBasicMutex
Definition qmutex.h:360
static Q_CONSTINIT QBasicMutex destructorsMutex
#define DEBUG_MSG
QList< void(*)(void *)> DestructorMap