Qt 4.8
qmutex_unix.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 "qstring.h"
45 
46 #ifndef QT_NO_THREAD
47 #include "qatomic.h"
48 #include "qmutex_p.h"
49 
50 #include <errno.h>
51 
52 #if defined(Q_OS_VXWORKS) && defined(wakeup)
53 #undef wakeup
54 #endif
55 
56 #if defined(Q_OS_MAC)
57 # include <mach/mach.h>
58 # include <mach/task.h>
59 #elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
60 # include <linux/futex.h>
61 # include <sys/syscall.h>
62 # include <unistd.h>
63 # include <QtCore/qelapsedtimer.h>
64 #endif
65 
67 
68 #if !defined(Q_OS_LINUX) || defined(QT_LINUXBASE)
69 static void report_error(int code, const char *where, const char *what)
70 {
71  if (code != 0)
72  qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code)));
73 }
74 #endif
75 
76 
78  : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0)
79 {
80 #if !defined(Q_OS_LINUX) || defined(QT_LINUXBASE)
81  wakeup = false;
82  report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init");
83  report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init");
84 #endif
85 }
86 
88 {
89 #if !defined(Q_OS_LINUX) || defined(QT_LINUXBASE)
90  report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy");
91  report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy");
92 #endif
93 }
94 
95 #if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
96 
97 static inline int _q_futex(volatile int *addr, int op, int val, const struct timespec *timeout, int *addr2, int val2)
98 {
99  return syscall(SYS_futex, addr, op, val, timeout, addr2, val2);
100 }
101 
102 bool QMutexPrivate::wait(int timeout)
103 {
104  struct timespec ts, *pts = 0;
106  if (timeout >= 0) {
107  ts.tv_nsec = ((timeout % 1000) * 1000) * 1000;
108  ts.tv_sec = (timeout / 1000);
109  pts = &ts;
110  timer.start();
111  }
112  while (contenders.fetchAndStoreAcquire(2) > 0) {
113  int r = _q_futex(&contenders._q_value, FUTEX_WAIT, 2, pts, 0, 0);
114  if (r != 0 && errno == ETIMEDOUT)
115  return false;
116 
117  if (pts) {
118  // recalculate the timeout
119  qint64 xtimeout = timeout * 1000 * 1000;
120  xtimeout -= timer.nsecsElapsed();
121  if (xtimeout < 0) {
122  // timer expired after we returned
123  return false;
124  }
125 
126  ts.tv_sec = xtimeout / Q_INT64_C(1000) / 1000 / 1000;
127  ts.tv_nsec = xtimeout % (Q_INT64_C(1000) * 1000 * 1000);
128  }
129  }
130  return true;
131 }
132 
134 {
136  (void) _q_futex(&contenders._q_value, FUTEX_WAKE, 1, 0, 0, 0);
137 }
138 
139 #else // !Q_OS_LINUX || QT_LINUXBASE
140 
141 bool QMutexPrivate::wait(int timeout)
142 {
143  if (contenders.fetchAndAddAcquire(1) == 0) {
144  // lock acquired without waiting
145  return true;
146  }
147  report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock");
148  int errorCode = 0;
149  while (!wakeup) {
150  if (timeout < 0) {
151  errorCode = pthread_cond_wait(&cond, &mutex);
152  } else {
153  struct timeval tv;
154  gettimeofday(&tv, 0);
155 
156  timespec ti;
157  ti.tv_nsec = (tv.tv_usec + (timeout % 1000) * 1000) * 1000;
158  ti.tv_sec = tv.tv_sec + (timeout / 1000) + (ti.tv_nsec / 1000000000);
159  ti.tv_nsec %= 1000000000;
160 
161  errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
162  }
163  if (errorCode) {
164  if (errorCode == ETIMEDOUT) {
165  if (wakeup)
166  errorCode = 0;
167  break;
168  }
169  report_error(errorCode, "QMutex::lock()", "cv wait");
170  }
171  }
172  wakeup = false;
173  report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock");
174  contenders.deref();
175  return errorCode == 0;
176 }
177 
179 {
180  report_error(pthread_mutex_lock(&mutex), "QMutex::unlock", "mutex lock");
181  wakeup = true;
182  report_error(pthread_cond_signal(&cond), "QMutex::unlock", "cv signal");
183  report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
184 }
185 
186 #endif // !Q_OS_LINUX || QT_LINUXBASE
187 
189 
190 #endif // QT_NO_THREAD
QAtomicInt contenders
Definition: qmutex.h:158
QString qt_error_string(int errorCode)
Definition: qglobal.cpp:2600
volatile int _q_value
Definition: qbasicatomic.h:64
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
bool wait(int timeout=-1)
EventLoopTimerRef timer
static QWidget * owner
The QElapsedTimer class provides a fast way to calculate elapsed times.
Definition: qelapsedtimer.h:53
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
bool deref()
Atomically decrements the value of this QAtomicInt.
Q_CORE_EXPORT void qWarning(const char *,...)
static int _q_futex(volatile int *addr, int op, int val, const struct timespec *timeout, int *addr2, int val2)
Definition: qmutex_unix.cpp:97
qint64 nsecsElapsed() const
Returns the number of nanoseconds since this QElapsedTimer was last started.
__int64 qint64
Definition: qglobal.h:942
int fetchAndAddAcquire(int valueToAdd)
Atomic fetch-and-add.
#define Q_INT64_C(c)
Definition: qglobal.h:940
RecursionMode
Definition: qmutex.h:66
static void report_error(int code, const char *where, const char *what)
int fetchAndStoreAcquire(int newValue)
Atomic fetch-and-store.
QMutexPrivate(QMutex::RecursionMode mode)
Definition: qmutex_unix.cpp:77
#define qPrintable(string)
Definition: qglobal.h:1750
void start()
Starts this timer.
int fetchAndStoreRelease(int newValue)
Atomic fetch-and-store.
int errno