Qt 4.8
qthreadstorage.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qthreadstorage.h"
43 
44 #ifndef QT_NO_THREAD
45 #include "qthread.h"
46 #include "qthread_p.h"
47 #include "qmutex.h"
48 
49 #include <string.h>
50 
52 
53 // #define THREADSTORAGE_DEBUG
54 #ifdef THREADSTORAGE_DEBUG
55 # define DEBUG_MSG qtsDebug
56 
57 # include <stdio.h>
58 # include <stdarg.h>
59 void qtsDebug(const char *fmt, ...)
60 {
61  va_list va;
62  va_start(va, fmt);
63 
64  fprintf(stderr, "QThreadStorage: ");
65  vfprintf(stderr, fmt, va);
66  fprintf(stderr, "\n");
67 
68  va_end(va);
69 }
70 #else
71 # define DEBUG_MSG if(false)qDebug
72 #endif
73 
74 Q_GLOBAL_STATIC(QMutex, mutex)
75 typedef QVector<void (*)(void *)> DestructorMap;
76 Q_GLOBAL_STATIC(DestructorMap, destructors)
77 
78 QThreadStorageData::QThreadStorageData(void (*func)(void *))
79 {
80  QMutexLocker locker(mutex());
81  DestructorMap *destr = destructors();
82  if (!destr) {
83  /*
84  the destructors vector has already been destroyed, yet a new
85  QThreadStorage is being allocated. this can only happen during global
86  destruction, at which point we assume that there is only one thread.
87  in order to keep QThreadStorage working, we need somewhere to store
88  the data, best place we have in this situation is at the tail of the
89  current thread's tls vector. the destructor is ignored, since we have
90  no where to store it, and no way to actually call it.
91  */
93  id = data->tls.count();
94  DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p cannot be stored", id, func);
95  return;
96  }
97  for (id = 0; id < destr->count(); id++) {
98  if (destr->at(id) == 0)
99  break;
100  }
101  if (id == destr->count()) {
102  destr->append(func);
103  } else {
104  (*destr)[id] = func;
105  }
106  DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p", id, func);
107 }
108 
110 {
111  DEBUG_MSG("QThreadStorageData: Released id %d", id);
112  QMutexLocker locker(mutex());
113  if (destructors())
114  (*destructors())[id] = 0;
115 }
116 
118 {
120  if (!data) {
121  qWarning("QThreadStorage::get: QThreadStorage can only be used with threads started with QThread");
122  return 0;
123  }
124  QVector<void *> &tls = data->tls;
125  if (tls.size() <= id)
126  tls.resize(id + 1);
127  void **v = &tls[id];
128 
129  DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p",
130  id,
131  *v,
132  data->thread);
133 
134  return *v ? v : 0;
135 }
136 
137 void **QThreadStorageData::set(void *p)
138 {
140  if (!data) {
141  qWarning("QThreadStorage::set: QThreadStorage can only be used with threads started with QThread");
142  return 0;
143  }
144  QVector<void *> &tls = data->tls;
145  if (tls.size() <= id)
146  tls.resize(id + 1);
147 
148  void *&value = tls[id];
149  // delete any previous data
150  if (value != 0) {
151  DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p",
152  id,
153  value,
154  data->thread);
155 
156  QMutexLocker locker(mutex());
157  DestructorMap *destr = destructors();
158  void (*destructor)(void *) = destr ? destr->value(id) : 0;
159  locker.unlock();
160 
161  void *q = value;
162  value = 0;
163 
164  if (destructor)
165  destructor(q);
166  }
167 
168  // store new data
169  value = p;
170  DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread, p);
171  return &value;
172 }
173 
175 {
176  QVector<void *> *tls = reinterpret_cast<QVector<void *> *>(p);
177  if (!tls || tls->isEmpty() || !mutex())
178  return; // nothing to do
179 
180  DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread());
181  while (!tls->isEmpty()) {
182  void *&value = tls->last();
183  void *q = value;
184  value = 0;
185  int i = tls->size() - 1;
186  tls->resize(i);
187 
188  if (!q) {
189  // data already deleted
190  continue;
191  }
192 
193  QMutexLocker locker(mutex());
194  void (*destructor)(void *) = destructors()->value(i);
195  locker.unlock();
196 
197  if (!destructor) {
199  qWarning("QThreadStorage: Thread %p exited after QThreadStorage %d destroyed",
201  continue;
202  }
203  destructor(q); //crash here might mean the thread exited after qthreadstorage was destroyed
204 
205  if (tls->size() > i) {
206  //re reset the tls in case it has been recreated by its own destructor.
207  (*tls)[i] = 0;
208  }
209  }
210  tls->clear();
211 }
212 
348 #endif // QT_NO_THREAD
349 
static void finish(void **)
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
The QMutex class provides access serialization between threads.
Definition: qmutex.h:60
#define DEBUG_MSG
int count(const T &t) const
Returns the number of occurrences of value in the vector.
Definition: qvector.h:742
void unlock()
Unlocks this mutex locker.
Definition: qmutex.h:117
QVector< void * > tls
Definition: qthread_p.h:268
The QVector class is a template class that provides a dynamic array.
Definition: qdatastream.h:64
static QThread * currentThread()
Returns a pointer to a QThread which manages the currently executing thread.
Definition: qthread.cpp:419
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
T value(int i) const
Returns the value at index position i in the vector.
Definition: qvector.h:559
#define Q_GLOBAL_STATIC(TYPE, NAME)
Declares a global static variable with the given type and name.
Definition: qglobal.h:1968
void append(const T &t)
Inserts value at the end of the vector.
Definition: qvector.h:573
Q_CORE_EXPORT void qWarning(const char *,...)
static const char * data(const QByteArray &arr)
void ** get() const
const T & at(int i) const
Returns the item at index position i in the vector.
Definition: qvector.h:350
void ** set(void *p)
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
static QThreadData * current()
QThread * thread
Definition: qthread_p.h:260
QThreadStorage< QSharedNetworkSessionManager * > tls