Qt 4.8
qmutex.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 "qplatformdefs.h"
43 #include "qmutex.h"
44 #include <qdebug.h>
45 
46 #ifndef QT_NO_THREAD
47 #include "qatomic.h"
48 #include "qelapsedtimer.h"
49 #include "qthread.h"
50 #include "qmutex_p.h"
51 
53 
128  : d(new QMutexPrivate(mode))
129 { }
130 
137 { delete static_cast<QMutexPrivate *>(d); }
138 
152 {
153  QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
154  Qt::HANDLE self;
155 
156  if (d->recursive) {
157  self = QThread::currentThreadId();
158  if (d->owner == self) {
159  ++d->count;
160  Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter");
161  return;
162  }
163 
164  bool isLocked = d->contenders.testAndSetAcquire(0, 1);
165  if (!isLocked) {
166  // didn't get the lock, wait for it
167  isLocked = d->wait();
168  Q_ASSERT_X(isLocked, "QMutex::lock",
169  "Internal error, infinite wait has timed out.");
170  }
171 
172  d->owner = self;
173  ++d->count;
174  Q_ASSERT_X(d->count != 0, "QMutex::lock", "Overflow in recursion counter");
175  return;
176  }
177 
178  bool isLocked = d->contenders.testAndSetAcquire(0, 1);
179  if (!isLocked) {
180  lockInternal();
181  }
182 }
183 
202 {
203  QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
204  Qt::HANDLE self;
205 
206  if (d->recursive) {
207  self = QThread::currentThreadId();
208  if (d->owner == self) {
209  ++d->count;
210  Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
211  return true;
212  }
213 
214  bool isLocked = d->contenders.testAndSetAcquire(0, 1);
215  if (!isLocked) {
216  // some other thread has the mutex locked, or we tried to
217  // recursively lock an non-recursive mutex
218  return isLocked;
219  }
220 
221  d->owner = self;
222  ++d->count;
223  Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
224  return isLocked;
225  }
226 
227  return d->contenders.testAndSetAcquire(0, 1);
228 }
229 
256 bool QMutex::tryLock(int timeout)
257 {
258  QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
259  Qt::HANDLE self;
260 
261  if (d->recursive) {
262  self = QThread::currentThreadId();
263  if (d->owner == self) {
264  ++d->count;
265  Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
266  return true;
267  }
268 
269  bool isLocked = d->contenders.testAndSetAcquire(0, 1);
270  if (!isLocked) {
271  // didn't get the lock, wait for it
272  isLocked = d->wait(timeout);
273  if (!isLocked)
274  return false;
275  }
276 
277  d->owner = self;
278  ++d->count;
279  Q_ASSERT_X(d->count != 0, "QMutex::tryLock", "Overflow in recursion counter");
280  return true;
281  }
282 
283  return (d->contenders.testAndSetAcquire(0, 1)
284  // didn't get the lock, wait for it
285  || d->wait(timeout));
286 }
287 
288 
297 {
298  QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
299  if (d->recursive) {
300  if (!--d->count) {
301  d->owner = 0;
302  if (!d->contenders.testAndSetRelease(1, 0))
303  d->wakeUp();
304  }
305  } else {
306  if (!d->contenders.testAndSetRelease(1, 0))
307  d->wakeUp();
308  }
309 }
310 
455 {
456  QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d);
457 
458  if (QThread::idealThreadCount() == 1) {
459  // don't spin on single cpu machines
460  bool isLocked = d->wait();
461  Q_ASSERT_X(isLocked, "QMutex::lock",
462  "Internal error, infinite wait has timed out.");
463  Q_UNUSED(isLocked);
464  return;
465  }
466 
467  QElapsedTimer elapsedTimer;
468  elapsedTimer.start();
469  do {
470  qint64 spinTime = elapsedTimer.nsecsElapsed();
471  if (spinTime > d->maximumSpinTime) {
472  // didn't get the lock, wait for it, since we're not going to gain anything by spinning more
473  elapsedTimer.start();
474  bool isLocked = d->wait();
475  Q_ASSERT_X(isLocked, "QMutex::lock",
476  "Internal error, infinite wait has timed out.");
477  Q_UNUSED(isLocked);
478 
479  qint64 maximumSpinTime = d->maximumSpinTime;
480  qint64 averageWaitTime = d->averageWaitTime;
481  qint64 actualWaitTime = elapsedTimer.nsecsElapsed();
482  if (actualWaitTime < (QMutexPrivate::MaximumSpinTimeThreshold * 3 / 2)) {
483  // measure the wait times
484  averageWaitTime = d->averageWaitTime = qMin((averageWaitTime + actualWaitTime) / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold));
485  }
486 
487  // adjust the spin count when spinning does not benefit contention performance
489  // long waits, stop spinning
490  d->maximumSpinTime = 0;
491  } else {
492  // allow spinning if wait times decrease, but never spin more than the average wait time (otherwise we may perform worse)
493  d->maximumSpinTime = qBound(qint64(averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold));
494  }
495  return;
496  }
497  // be a good citizen... yielding lets something else run if there is something to run, but may also relieve memory pressure if not
499  } while (d->contenders != 0 || !d->contenders.testAndSetAcquire(0, 1));
500 
501  // spinning is working, do not change the spin time (unless we are using much less time than allowed to spin)
502  qint64 maximumSpinTime = d->maximumSpinTime;
503  qint64 spinTime = elapsedTimer.nsecsElapsed();
504  if (spinTime < maximumSpinTime / 2) {
505  // we are using much less time than we need, adjust the limit
507  }
508 }
509 
514 {
515  static_cast<QMutexPrivate *>(d)->wakeUp();
516 }
517 
538 
539 #endif // QT_NO_THREAD
double d
Definition: qnumeric_p.h:62
QAtomicInt contenders
Definition: qmutex.h:158
QMutexData * d
Definition: qmutex.h:98
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
void lock()
Locks the mutex.
Definition: qmutex.cpp:151
bool testAndSetAcquire(int expectedValue, int newValue)
Atomic test-and-set.
bool wait(int timeout=-1)
volatile qint64 maximumSpinTime
Definition: qmutex_p.h:77
The QElapsedTimer class provides a fast way to calculate elapsed times.
Definition: qelapsedtimer.h:53
bool testAndSetRelease(int expectedValue, int newValue)
Atomic test-and-set.
static void yieldCurrentThread()
Yields execution of the current thread to another runnable thread, if any.
~QMutex()
Destroys the mutex.
Definition: qmutex.cpp:136
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
void unlockInternal()
Definition: qmutex.cpp:513
qint64 nsecsElapsed() const
Returns the number of nanoseconds since this QElapsedTimer was last started.
__int64 qint64
Definition: qglobal.h:942
void * HANDLE
Definition: qnamespace.h:1671
const uint recursive
Definition: qmutex.h:159
void unlock()
Unlocks the mutex.
Definition: qmutex.cpp:296
#define Q_ASSERT_X(cond, where, what)
Definition: qglobal.h:1837
RecursionMode
Definition: qmutex.h:66
Q_DECL_CONSTEXPR const T & qBound(const T &min, const T &val, const T &max)
Definition: qglobal.h:1219
volatile qint64 averageWaitTime
Definition: qmutex_p.h:78
uint count
Definition: qmutex_p.h:80
bool tryLock()
Attempts to lock the mutex.
Definition: qmutex.cpp:201
QMutex(RecursionMode mode=NonRecursive)
Constructs a new mutex.
Definition: qmutex.cpp:127
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
void start()
Starts this timer.
Qt::HANDLE owner
Definition: qmutex_p.h:79
void lockInternal()
Definition: qmutex.cpp:454
static Qt::HANDLE currentThreadId()
Returns the thread handle of the currently executing thread.
static int idealThreadCount()
Returns the ideal number of threads that can be run on the system.