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

source code of qtbase/src/corelib/thread/qthreadstorage.cpp