thread/qmutex_linux.cpp

Source codeSwitch to Preprocessed file
LineSource CodeCoverage
1/**************************************************************************** -
2** -
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -
4** Copyright (C) 2012 Intel Corporation -
5** Contact: http://www.qt-project.org/legal -
6** -
7** This file is part of the QtCore module of the Qt Toolkit. -
8** -
9** $QT_BEGIN_LICENSE:LGPL$ -
10** Commercial License Usage -
11** Licensees holding valid commercial Qt licenses may use this file in -
12** accordance with the commercial license agreement provided with the -
13** Software or, alternatively, in accordance with the terms contained in -
14** a written agreement between you and Digia. For licensing terms and -
15** conditions see http://qt.digia.com/licensing. For further information -
16** use the contact form at http://qt.digia.com/contact-us. -
17** -
18** GNU Lesser General Public License Usage -
19** Alternatively, this file may be used under the terms of the GNU Lesser -
20** General Public License version 2.1 as published by the Free Software -
21** Foundation and appearing in the file LICENSE.LGPL included in the -
22** packaging of this file. Please review the following information to -
23** ensure the GNU Lesser General Public License version 2.1 requirements -
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -
25** -
26** In addition, as a special exception, Digia gives you certain additional -
27** rights. These rights are described in the Digia Qt LGPL Exception -
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -
29** -
30** GNU General Public License Usage -
31** Alternatively, this file may be used under the terms of the GNU -
32** General Public License version 3.0 as published by the Free Software -
33** Foundation and appearing in the file LICENSE.GPL included in the -
34** packaging of this file. Please review the following information to -
35** ensure the GNU General Public License version 3.0 requirements will be -
36** met: http://www.gnu.org/copyleft/gpl.html. -
37** -
38** -
39** $QT_END_LICENSE$ -
40** -
41****************************************************************************/ -
42 -
43#include "qplatformdefs.h" -
44#include "qmutex.h" -
45 -
46#ifndef QT_NO_THREAD -
47#include "qatomic.h" -
48#include "qmutex_p.h" -
49#include "qelapsedtimer.h" -
50 -
51#include <linux/futex.h> -
52#include <sys/syscall.h> -
53#include <unistd.h> -
54#include <errno.h> -
55#include <asm/unistd.h> -
56 -
57#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L -
58// C++11 mode -
59# include <type_traits> -
60 -
61static void checkElapsedTimerIsTrivial() -
62{ -
63 Q_STATIC_ASSERT(std::has_trivial_default_constructor<QT_PREPEND_NAMESPACE(QElapsedTimer)>::value);
executed (the execution status of this line is deduced): static_assert(bool(std::has_trivial_default_constructor< ::QElapsedTimer>::value), "std::has_trivial_default_constructor<QT_PREPEND_NAMESPACE(QElapsedTimer)>::value");
-
64}
executed: }
Execution Count:4631415
4631415
65 -
66#else -
67static void checkElapsedTimerIsTrivial() -
68{ -
69} -
70#endif -
71 -
72#ifndef QT_LINUX_FUTEX -
73# error "Qt build is broken: qmutex_linux.cpp is being built but futex support is not wanted" -
74#endif -
75 -
76QT_BEGIN_NAMESPACE -
77 -
78/* -
79 * QBasicMutex implementation on Linux with futexes -
80 * -
81 * QBasicMutex contains one pointer value, which can contain one of four -
82 * different values: -
83 * 0x0 unlocked, non-recursive mutex -
84 * 0x1 locked non-recursive mutex, no waiters -
85 * 0x3 locked non-recursive mutex, at least one waiter -
86 * > 0x3 recursive mutex, points to a QMutexPrivate object -
87 * -
88 * LOCKING (non-recursive): -
89 * -
90 * A non-recursive mutex starts in the 0x0 state, indicating that it's -
91 * unlocked. When the first thread attempts to lock it, it will perform a -
92 * testAndSetAcquire from 0x0 to 0x1. If that succeeds, the caller concludes -
93 * that it successfully locked the mutex. That happens in fastTryLock(). -
94 * -
95 * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called. -
96 * -
97 * lockInternal will examine the value of the pointer. Otherwise, it will use -
98 * futexes to sleep and wait for another thread to unlock. To do that, it needs -
99 * to set a pointer value of 0x3, which indicates that thread is waiting. It -
100 * does that by a simple fetchAndStoreAcquire operation. -
101 * -
102 * If the pointer value was 0x0, it means we succeeded in acquiring the mutex. -
103 * For other values, it will then call FUTEX_WAIT and with an expected value of -
104 * 0x3. -
105 * -
106 * If the pointer value changed before futex(2) managed to sleep, it will -
107 * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we -
108 * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we -
109 * start over again. -
110 * -
111 * UNLOCKING (non-recursive): -
112 * -
113 * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The -
114 * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that -
115 * succeeds, we're done. -
116 * -
117 * If it fails, unlockInternal() is called. The only possibility is that the -
118 * mutex value was 0x3, which indicates some other thread is waiting or was -
119 * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE. -
120 */ -
121 -
122static QBasicAtomicInt futexFlagSupport = Q_BASIC_ATOMIC_INITIALIZER(-1); -
123 -
124static int checkFutexPrivateSupport() -
125{ -
126 int value = 0;
executed (the execution status of this line is deduced): int value = 0;
-
127#if defined(FUTEX_PRIVATE_FLAG) -
128 // check if the kernel supports extra futex flags -
129 // FUTEX_PRIVATE_FLAG appeared in v2.6.22 -
130 Q_STATIC_ASSERT(FUTEX_PRIVATE_FLAG != 0x80000000);
executed (the execution status of this line is deduced): static_assert(bool(128 != 0x80000000), "FUTEX_PRIVATE_FLAG != 0x80000000");
-
131 -
132 // try an operation that has no side-effects: wake up 42 threads -
133 // futex will return -1 (errno==ENOSYS) if the flag isn't supported -
134 // there should be no other error conditions -
135 value = syscall(__NR_futex, &futexFlagSupport,
executed (the execution status of this line is deduced): value = syscall(202, &futexFlagSupport,
-
136 FUTEX_WAKE | FUTEX_PRIVATE_FLAG,
executed (the execution status of this line is deduced): 1 | 128,
-
137 42, 0, 0, 0);
executed (the execution status of this line is deduced): 42, 0, 0, 0);
-
138 if (value != -1)
partially evaluated: value != -1
TRUEFALSE
yes
Evaluation Count:74
no
Evaluation Count:0
0-74
139 value = FUTEX_PRIVATE_FLAG;
executed: value = 128;
Execution Count:74
74
140 else -
141 value = 0;
never executed: value = 0;
0
142 -
143#else -
144 value = 0; -
145#endif -
146 futexFlagSupport.store(value);
executed (the execution status of this line is deduced): futexFlagSupport.store(value);
-
147 return value;
executed: return value;
Execution Count:73
73
148} -
149 -
150static inline int futexFlags() -
151{ -
152 int value = futexFlagSupport.load();
executed (the execution status of this line is deduced): int value = futexFlagSupport.load();
-
153 if (Q_LIKELY(value != -1))
evaluated: __builtin_expect(!!(value != -1), true)
TRUEFALSE
yes
Evaluation Count:14715604
yes
Evaluation Count:72
72-14715604
154 return value;
executed: return value;
Execution Count:14705438
14705438
155 return checkFutexPrivateSupport();
executed: return checkFutexPrivateSupport();
Execution Count:73
73
156} -
157 -
158static inline int _q_futex(void *addr, int op, int val, const struct timespec *timeout) Q_DECL_NOTHROW -
159{ -
160 volatile int *int_addr = reinterpret_cast<volatile int *>(addr);
executed (the execution status of this line is deduced): volatile int *int_addr = reinterpret_cast<volatile int *>(addr);
-
161#if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE == 8 -
162 int_addr++; //We want a pointer to the 32 least significant bit of QMutex::d -
163#endif -
164 int *addr2 = 0;
executed (the execution status of this line is deduced): int *addr2 = 0;
-
165 int val2 = 0;
executed (the execution status of this line is deduced): int val2 = 0;
-
166 -
167 // we use __NR_futex because some libcs (like Android's bionic) don't -
168 // provide SYS_futex etc. -
169 return syscall(__NR_futex, int_addr, op | futexFlags(), val, timeout, addr2, val2);
executed: return syscall(202, int_addr, op | futexFlags(), val, timeout, addr2, val2);
Execution Count:14722852
14722852
170} -
171 -
172static inline QMutexData *dummyFutexValue() -
173{ -
174 return reinterpret_cast<QMutexData *>(quintptr(3));
executed: return reinterpret_cast<QMutexData *>(quintptr(3));
Execution Count:18492938
18492938
175} -
176 -
177template <bool IsTimed> static inline -
178bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = -1) Q_DECL_NOTHROW -
179{ -
180 if (!IsTimed)
evaluated: !IsTimed
TRUEFALSE
yes
Evaluation Count:4620449
yes
Evaluation Count:232452
232452-4620449
181 timeout = -1;
executed: timeout = -1;
Execution Count:4618076
4618076
182 -
183 // we're here because fastTryLock() has just failed -
184 if (timeout == 0)
evaluated: timeout == 0
TRUEFALSE
yes
Evaluation Count:215910
yes
Evaluation Count:4631859
215910-4631859
185 return false;
executed: return false;
Execution Count:215393
215393
186 -
187 struct timespec ts, *pts = 0;
executed (the execution status of this line is deduced): struct timespec ts, *pts = 0;
-
188 QElapsedTimer elapsedTimer;
executed (the execution status of this line is deduced): QElapsedTimer elapsedTimer;
-
189 checkElapsedTimerIsTrivial();
executed (the execution status of this line is deduced): checkElapsedTimerIsTrivial();
-
190 if (IsTimed && timeout > 0) {
evaluated: IsTimed
TRUEFALSE
yes
Evaluation Count:16831
yes
Evaluation Count:4615492
evaluated: timeout > 0
TRUEFALSE
yes
Evaluation Count:16828
yes
Evaluation Count:4
4-4615492
191 ts.tv_sec = timeout / 1000;
executed (the execution status of this line is deduced): ts.tv_sec = timeout / 1000;
-
192 ts.tv_nsec = (timeout % 1000) * 1000 * 1000;
executed (the execution status of this line is deduced): ts.tv_nsec = (timeout % 1000) * 1000 * 1000;
-
193 elapsedTimer.start();
executed (the execution status of this line is deduced): elapsedTimer.start();
-
194 }
executed: }
Execution Count:16838
16838
195 -
196 // the mutex is locked already, set a bit indicating we're waiting -
197 while (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != 0) {
evaluated: d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != 0
TRUEFALSE
yes
Evaluation Count:7011362
yes
Evaluation Count:4650469
4650469-7011362
198 if (IsTimed && pts == &ts) {
evaluated: IsTimed
TRUEFALSE
yes
Evaluation Count:16684
yes
Evaluation Count:7000305
evaluated: pts == &ts
TRUEFALSE
yes
Evaluation Count:292
yes
Evaluation Count:16389
292-7000305
199 // recalculate the timeout -
200 qint64 xtimeout = qint64(timeout) * 1000 * 1000;
executed (the execution status of this line is deduced): qint64 xtimeout = qint64(timeout) * 1000 * 1000;
-
201 xtimeout -= elapsedTimer.nsecsElapsed();
executed (the execution status of this line is deduced): xtimeout -= elapsedTimer.nsecsElapsed();
-
202 if (xtimeout <= 0) {
evaluated: xtimeout <= 0
TRUEFALSE
yes
Evaluation Count:146
yes
Evaluation Count:146
146
203 // timer expired after we returned -
204 return false;
executed: return false;
Execution Count:146
146
205 } -
206 ts.tv_sec = xtimeout / Q_INT64_C(1000) / 1000 / 1000;
executed (the execution status of this line is deduced): ts.tv_sec = xtimeout / static_cast<long long>(1000LL) / 1000 / 1000;
-
207 ts.tv_nsec = xtimeout % (Q_INT64_C(1000) * 1000 * 1000);
executed (the execution status of this line is deduced): ts.tv_nsec = xtimeout % (static_cast<long long>(1000LL) * 1000 * 1000);
-
208 }
executed: }
Execution Count:146
146
209 if (IsTimed && timeout > 0)
evaluated: IsTimed
TRUEFALSE
yes
Evaluation Count:16537
yes
Evaluation Count:7002417
evaluated: timeout > 0
TRUEFALSE
yes
Evaluation Count:16533
yes
Evaluation Count:4
4-7002417
210 pts = &ts;
executed: pts = &ts;
Execution Count:16535
16535
211 -
212 // successfully set the waiting bit, now sleep -
213 int r = _q_futex(&d_ptr, FUTEX_WAIT, quintptr(dummyFutexValue()), pts);
executed (the execution status of this line is deduced): int r = _q_futex(&d_ptr, 0, quintptr(dummyFutexValue()), pts);
-
214 if (IsTimed && r != 0 && errno == ETIMEDOUT)
evaluated: IsTimed
TRUEFALSE
yes
Evaluation Count:16532
yes
Evaluation Count:7003013
evaluated: r != 0
TRUEFALSE
yes
Evaluation Count:8035
yes
Evaluation Count:8501
evaluated: (*__errno_location ()) == 110
TRUEFALSE
yes
Evaluation Count:7903
yes
Evaluation Count:135
135-7003013
215 return false;
executed: return false;
Execution Count:7898
7898
216 -
217 // we got woken up, so try to acquire the mutex -
218 // note we must set to dummyFutexValue because there could be other threads -
219 // also waiting -
220 }
executed: }
Execution Count:7011219
7011219
221 -
222 Q_ASSERT(d_ptr.load());
executed (the execution status of this line is deduced): qt_noop();
-
223 return true;
executed: return true;
Execution Count:4650436
4650436
224} -
225 -
226void QBasicMutex::lockInternal() Q_DECL_NOTHROW -
227{ -
228 Q_ASSERT(!isRecursive());
executed (the execution status of this line is deduced): qt_noop();
-
229 lockInternal_helper<false>(d_ptr);
executed (the execution status of this line is deduced): lockInternal_helper<false>(d_ptr);
-
230}
executed: }
Execution Count:4641658
4641658
231 -
232bool QBasicMutex::lockInternal(int timeout) Q_DECL_NOTHROW -
233{ -
234 Q_ASSERT(!isRecursive());
executed (the execution status of this line is deduced): qt_noop();
-
235 return lockInternal_helper<true>(d_ptr, timeout);
executed: return lockInternal_helper<true>(d_ptr, timeout);
Execution Count:236010
236010
236} -
237 -
238void QBasicMutex::unlockInternal() Q_DECL_NOTHROW -
239{ -
240 QMutexData *d = d_ptr.load();
executed (the execution status of this line is deduced): QMutexData *d = d_ptr.load();
-
241 Q_ASSERT(d); //we must be locked
executed (the execution status of this line is deduced): qt_noop();
-
242 Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
executed (the execution status of this line is deduced): qt_noop();
-
243 Q_UNUSED(d);
executed (the execution status of this line is deduced): (void)d;;
-
244 Q_ASSERT(!isRecursive());
executed (the execution status of this line is deduced): qt_noop();
-
245 -
246 d_ptr.storeRelease(0);
executed (the execution status of this line is deduced): d_ptr.storeRelease(0);
-
247 _q_futex(&d_ptr, FUTEX_WAKE, 1, 0);
executed (the execution status of this line is deduced): _q_futex(&d_ptr, 1, 1, 0);
-
248}
executed: }
Execution Count:7868264
7868264
249 -
250 -
251QT_END_NAMESPACE -
252 -
253#endif // QT_NO_THREAD -
254 -
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial