1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the test suite of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <QtTest/QtTest> |
31 | |
32 | #include <qatomic.h> |
33 | #include <qcoreapplication.h> |
34 | #include <qelapsedtimer.h> |
35 | #include <qmutex.h> |
36 | #include <qthread.h> |
37 | #include <qwaitcondition.h> |
38 | |
39 | class tst_QMutex : public QObject |
40 | { |
41 | Q_OBJECT |
42 | public: |
43 | enum class TimeUnit { |
44 | Nanoseconds, |
45 | Microseconds, |
46 | Milliseconds, |
47 | Seconds, |
48 | }; |
49 | Q_ENUM(TimeUnit); |
50 | |
51 | private slots: |
52 | void convertToMilliseconds_data(); |
53 | void convertToMilliseconds(); |
54 | void tryLock_non_recursive(); |
55 | void try_lock_for_non_recursive(); |
56 | void try_lock_until_non_recursive(); |
57 | void tryLock_recursive(); |
58 | void try_lock_for_recursive(); |
59 | void try_lock_until_recursive(); |
60 | void lock_unlock_locked_tryLock(); |
61 | void stressTest(); |
62 | void tryLockRace(); |
63 | void tryLockDeadlock(); |
64 | void tryLockNegative_data(); |
65 | void tryLockNegative(); |
66 | void moreStress(); |
67 | }; |
68 | |
69 | static const int iterations = 100; |
70 | |
71 | QAtomicInt lockCount(0); |
72 | QMutex normalMutex; |
73 | QRecursiveMutex recursiveMutex; |
74 | QSemaphore testsTurn; |
75 | QSemaphore threadsTurn; |
76 | |
77 | /* |
78 | Depending on the OS, tryWaits may return early than expected because of the |
79 | resolution of the underlying timer is too coarse. E.g.: on Windows |
80 | WaitForSingleObjectEx does *not* use high resolution multimedia timers, and |
81 | it's actually very coarse, about 16msec by default. |
82 | */ |
83 | enum { |
84 | #ifdef Q_OS_WIN |
85 | systemTimersResolution = 16, |
86 | #else |
87 | systemTimersResolution = 1, |
88 | #endif |
89 | waitTime = 100 |
90 | }; |
91 | |
92 | #if __has_include(<chrono>) |
93 | static Q_CONSTEXPR std::chrono::milliseconds waitTimeAsDuration(waitTime); |
94 | #endif |
95 | |
96 | void tst_QMutex::convertToMilliseconds_data() |
97 | { |
98 | QTest::addColumn<TimeUnit>(name: "unit" ); |
99 | QTest::addColumn<double>(name: "doubleValue" ); |
100 | QTest::addColumn<qint64>(name: "intValue" ); |
101 | QTest::addColumn<qint64>(name: "expected" ); |
102 | |
103 | #if !__has_include(<chrono>) |
104 | QSKIP("This test requires <chrono>" ); |
105 | #endif |
106 | |
107 | auto add = [](TimeUnit unit, double d, long long i, qint64 expected) { |
108 | const QScopedArrayPointer<char> enumName(QTest::toString(t: unit)); |
109 | QTest::addRow(format: "%s:%f:%lld" , enumName.data(), d, i) |
110 | << unit << d << qint64(i) << expected; |
111 | }; |
112 | |
113 | auto forAllUnitsAdd = [=](double d, long long i, qint64 expected) { |
114 | for (auto unit : {TimeUnit::Nanoseconds, TimeUnit::Microseconds, TimeUnit::Milliseconds, TimeUnit::Seconds}) |
115 | add(unit, d, i, expected); |
116 | }; |
117 | |
118 | forAllUnitsAdd(-0.5, -1, 0); // all negative values result in 0 |
119 | |
120 | forAllUnitsAdd(0, 0, 0); |
121 | |
122 | add(TimeUnit::Nanoseconds, 1, 1, 1); |
123 | add(TimeUnit::Nanoseconds, 1000 * 1000, 1000 * 1000, 1); |
124 | add(TimeUnit::Nanoseconds, 1000 * 1000 + 0.5, 1000 * 1000 + 1, 2); |
125 | |
126 | add(TimeUnit::Microseconds, 1, 1, 1); |
127 | add(TimeUnit::Microseconds, 1000, 1000, 1); |
128 | add(TimeUnit::Microseconds, 1000 + 0.5, 1000 + 1, 2); |
129 | |
130 | add(TimeUnit::Milliseconds, 1, 1, 1); |
131 | add(TimeUnit::Milliseconds, 1.5, 2, 2); |
132 | |
133 | add(TimeUnit::Seconds, 0.9991, 1, 1000); |
134 | |
135 | // |
136 | // overflowing int results in INT_MAX (equivalent to a spurious wakeup after ~24 days); check it: |
137 | // |
138 | |
139 | // spot on: |
140 | add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000, INT_MAX * Q_INT64_C(1000) * 1000, INT_MAX); |
141 | add(TimeUnit::Microseconds, INT_MAX * 1000., INT_MAX * Q_INT64_C(1000), INT_MAX); |
142 | add(TimeUnit::Milliseconds, INT_MAX, INT_MAX, INT_MAX); |
143 | |
144 | // minimally above: |
145 | add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 + 1, INT_MAX * Q_INT64_C(1000) * 1000 + 1, INT_MAX); |
146 | add(TimeUnit::Microseconds, INT_MAX * 1000. + 1, INT_MAX * Q_INT64_C(1000) + 1, INT_MAX); |
147 | add(TimeUnit::Milliseconds, INT_MAX + 1., INT_MAX + Q_INT64_C(1), INT_MAX); |
148 | add(TimeUnit::Seconds, INT_MAX / 1000. + 1, INT_MAX / 1000 + 1, INT_MAX); |
149 | |
150 | // minimally below: |
151 | add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 - 1, INT_MAX * Q_INT64_C(1000) * 1000 - 1, INT_MAX); |
152 | add(TimeUnit::Microseconds, INT_MAX * 1000. - 1, INT_MAX * Q_INT64_C(1000) - 1, INT_MAX); |
153 | add(TimeUnit::Milliseconds, INT_MAX - 0.1, INT_MAX , INT_MAX); |
154 | |
155 | } |
156 | |
157 | void tst_QMutex::convertToMilliseconds() |
158 | { |
159 | #if !__has_include(<chrono>) |
160 | QSKIP("This test requires <chrono>" ); |
161 | #else |
162 | QFETCH(TimeUnit, unit); |
163 | QFETCH(double, doubleValue); |
164 | QFETCH(qint64, intValue); |
165 | QFETCH(qint64, expected); |
166 | |
167 | Q_CONSTEXPR qint64 maxShort = std::numeric_limits<short>::max(); |
168 | Q_CONSTEXPR qint64 maxInt = std::numeric_limits<int>::max(); |
169 | Q_CONSTEXPR qint64 maxUInt = std::numeric_limits<uint>::max(); |
170 | |
171 | switch (unit) { |
172 | #define CASE(Unit, Period) \ |
173 | case TimeUnit::Unit: \ |
174 | DO(double, Period, doubleValue); \ |
175 | if (intValue < maxShort) \ |
176 | DO(short, Period, short(intValue)); \ |
177 | if (intValue < maxInt) \ |
178 | DO(int, Period, int(intValue)); \ |
179 | DO(qint64, Period, intValue); \ |
180 | if (intValue >= 0) { \ |
181 | if (intValue < maxUInt) \ |
182 | DO(uint, Period, uint(intValue)); \ |
183 | DO(quint64, Period, quint64(intValue)); \ |
184 | } \ |
185 | break |
186 | #define DO(Rep, Period, val) \ |
187 | do { \ |
188 | const std::chrono::duration<Rep, Period> wait((val)); \ |
189 | QCOMPARE(QMutex::convertToMilliseconds(wait), expected); \ |
190 | } while (0) |
191 | |
192 | CASE(Nanoseconds, std::nano); |
193 | CASE(Microseconds, std::micro); |
194 | CASE(Milliseconds, std::milli); |
195 | CASE(Seconds, std::ratio<1>); |
196 | #undef DO |
197 | #undef CASE |
198 | } |
199 | #endif |
200 | } |
201 | |
202 | void tst_QMutex::tryLock_non_recursive() |
203 | { |
204 | class Thread : public QThread |
205 | { |
206 | public: |
207 | void run() |
208 | { |
209 | testsTurn.release(); |
210 | |
211 | // TEST 1: thread can't acquire lock |
212 | threadsTurn.acquire(); |
213 | QVERIFY(!normalMutex.tryLock()); |
214 | testsTurn.release(); |
215 | |
216 | // TEST 2: thread can acquire lock |
217 | threadsTurn.acquire(); |
218 | QVERIFY(normalMutex.tryLock()); |
219 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
220 | QVERIFY(!normalMutex.tryLock()); |
221 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
222 | normalMutex.unlock(); |
223 | testsTurn.release(); |
224 | |
225 | // TEST 3: thread can't acquire lock, timeout = waitTime |
226 | threadsTurn.acquire(); |
227 | QElapsedTimer timer; |
228 | timer.start(); |
229 | QVERIFY(!normalMutex.tryLock(waitTime)); |
230 | QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); |
231 | testsTurn.release(); |
232 | |
233 | // TEST 4: thread can acquire lock, timeout = waitTime |
234 | threadsTurn.acquire(); |
235 | timer.start(); |
236 | QVERIFY(normalMutex.tryLock(waitTime)); |
237 | QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution); |
238 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
239 | timer.start(); |
240 | // it's non-recursive, so the following lock needs to fail |
241 | QVERIFY(!normalMutex.tryLock(waitTime)); |
242 | QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); |
243 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
244 | normalMutex.unlock(); |
245 | testsTurn.release(); |
246 | |
247 | // TEST 5: thread can't acquire lock, timeout = 0 |
248 | threadsTurn.acquire(); |
249 | QVERIFY(!normalMutex.tryLock(0)); |
250 | testsTurn.release(); |
251 | |
252 | // TEST 6: thread can acquire lock, timeout = 0 |
253 | threadsTurn.acquire(); |
254 | timer.start(); |
255 | QVERIFY(normalMutex.tryLock(0)); |
256 | QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); |
257 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
258 | QVERIFY(!normalMutex.tryLock(0)); |
259 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
260 | normalMutex.unlock(); |
261 | testsTurn.release(); |
262 | |
263 | // TEST 7 overflow: thread can acquire lock, timeout = 3000 (QTBUG-24795) |
264 | threadsTurn.acquire(); |
265 | timer.start(); |
266 | QVERIFY(normalMutex.tryLock(3000)); |
267 | QVERIFY(timer.elapsed() < 3000 + systemTimersResolution); |
268 | normalMutex.unlock(); |
269 | testsTurn.release(); |
270 | |
271 | threadsTurn.acquire(); |
272 | } |
273 | }; |
274 | |
275 | Thread thread; |
276 | thread.start(); |
277 | |
278 | // TEST 1: thread can't acquire lock |
279 | testsTurn.acquire(); |
280 | normalMutex.lock(); |
281 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
282 | threadsTurn.release(); |
283 | |
284 | // TEST 2: thread can acquire lock |
285 | testsTurn.acquire(); |
286 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
287 | normalMutex.unlock(); |
288 | threadsTurn.release(); |
289 | |
290 | // TEST 3: thread can't acquire lock, timeout = waitTime |
291 | testsTurn.acquire(); |
292 | normalMutex.lock(); |
293 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
294 | threadsTurn.release(); |
295 | |
296 | // TEST 4: thread can acquire lock, timeout = waitTime |
297 | testsTurn.acquire(); |
298 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
299 | normalMutex.unlock(); |
300 | threadsTurn.release(); |
301 | |
302 | // TEST 5: thread can't acquire lock, timeout = 0 |
303 | testsTurn.acquire(); |
304 | normalMutex.lock(); |
305 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
306 | threadsTurn.release(); |
307 | |
308 | // TEST 6: thread can acquire lock, timeout = 0 |
309 | testsTurn.acquire(); |
310 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
311 | normalMutex.unlock(); |
312 | threadsTurn.release(); |
313 | |
314 | // TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795) |
315 | testsTurn.acquire(); |
316 | normalMutex.lock(); |
317 | threadsTurn.release(); |
318 | QThread::msleep(100); |
319 | normalMutex.unlock(); |
320 | |
321 | // wait for thread to finish |
322 | testsTurn.acquire(); |
323 | threadsTurn.release(); |
324 | thread.wait(); |
325 | } |
326 | |
327 | void tst_QMutex::try_lock_for_non_recursive() { |
328 | #if !__has_include(<chrono>) |
329 | QSKIP("This test requires <chrono>" ); |
330 | #else |
331 | class Thread : public QThread |
332 | { |
333 | public: |
334 | void run() |
335 | { |
336 | testsTurn.release(); |
337 | |
338 | // TEST 1: thread can't acquire lock |
339 | threadsTurn.acquire(); |
340 | QVERIFY(!normalMutex.try_lock()); |
341 | testsTurn.release(); |
342 | |
343 | // TEST 2: thread can acquire lock |
344 | threadsTurn.acquire(); |
345 | QVERIFY(normalMutex.try_lock()); |
346 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
347 | QVERIFY(!normalMutex.try_lock()); |
348 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
349 | normalMutex.unlock(); |
350 | testsTurn.release(); |
351 | |
352 | // TEST 3: thread can't acquire lock, timeout = waitTime |
353 | threadsTurn.acquire(); |
354 | QElapsedTimer timer; |
355 | timer.start(); |
356 | QVERIFY(!normalMutex.try_lock_for(waitTimeAsDuration)); |
357 | QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); |
358 | testsTurn.release(); |
359 | |
360 | // TEST 4: thread can acquire lock, timeout = waitTime |
361 | threadsTurn.acquire(); |
362 | timer.start(); |
363 | QVERIFY(normalMutex.try_lock_for(waitTimeAsDuration)); |
364 | QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution); |
365 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
366 | timer.start(); |
367 | // it's non-recursive, so the following lock needs to fail |
368 | QVERIFY(!normalMutex.try_lock_for(waitTimeAsDuration)); |
369 | QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); |
370 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
371 | normalMutex.unlock(); |
372 | testsTurn.release(); |
373 | |
374 | // TEST 5: thread can't acquire lock, timeout = 0 |
375 | threadsTurn.acquire(); |
376 | QVERIFY(!normalMutex.try_lock_for(std::chrono::milliseconds::zero())); |
377 | testsTurn.release(); |
378 | |
379 | // TEST 6: thread can acquire lock, timeout = 0 |
380 | threadsTurn.acquire(); |
381 | timer.start(); |
382 | QVERIFY(normalMutex.try_lock_for(std::chrono::milliseconds::zero())); |
383 | QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); |
384 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
385 | QVERIFY(!normalMutex.try_lock_for(std::chrono::milliseconds::zero())); |
386 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
387 | normalMutex.unlock(); |
388 | testsTurn.release(); |
389 | |
390 | // TEST 7 overflow: thread can acquire lock, timeout = 3000 (QTBUG-24795) |
391 | threadsTurn.acquire(); |
392 | timer.start(); |
393 | QVERIFY(normalMutex.try_lock_for(std::chrono::milliseconds(3000))); |
394 | QVERIFY(timer.elapsed() < 3000 + systemTimersResolution); |
395 | normalMutex.unlock(); |
396 | testsTurn.release(); |
397 | |
398 | threadsTurn.acquire(); |
399 | } |
400 | }; |
401 | |
402 | Thread thread; |
403 | thread.start(); |
404 | |
405 | // TEST 1: thread can't acquire lock |
406 | testsTurn.acquire(); |
407 | normalMutex.lock(); |
408 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
409 | threadsTurn.release(); |
410 | |
411 | // TEST 2: thread can acquire lock |
412 | testsTurn.acquire(); |
413 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
414 | normalMutex.unlock(); |
415 | threadsTurn.release(); |
416 | |
417 | // TEST 3: thread can't acquire lock, timeout = waitTime |
418 | testsTurn.acquire(); |
419 | normalMutex.lock(); |
420 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
421 | threadsTurn.release(); |
422 | |
423 | // TEST 4: thread can acquire lock, timeout = waitTime |
424 | testsTurn.acquire(); |
425 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
426 | normalMutex.unlock(); |
427 | threadsTurn.release(); |
428 | |
429 | // TEST 5: thread can't acquire lock, timeout = 0 |
430 | testsTurn.acquire(); |
431 | normalMutex.lock(); |
432 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
433 | threadsTurn.release(); |
434 | |
435 | // TEST 6: thread can acquire lock, timeout = 0 |
436 | testsTurn.acquire(); |
437 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
438 | normalMutex.unlock(); |
439 | threadsTurn.release(); |
440 | |
441 | // TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795) |
442 | testsTurn.acquire(); |
443 | normalMutex.lock(); |
444 | threadsTurn.release(); |
445 | QThread::msleep(100); |
446 | normalMutex.unlock(); |
447 | |
448 | // wait for thread to finish |
449 | testsTurn.acquire(); |
450 | threadsTurn.release(); |
451 | thread.wait(); |
452 | #endif |
453 | } |
454 | |
455 | void tst_QMutex::try_lock_until_non_recursive() |
456 | { |
457 | #if !__has_include(<chrono>) |
458 | QSKIP("This test requires <chrono>" ); |
459 | #else |
460 | class Thread : public QThread |
461 | { |
462 | public: |
463 | void run() |
464 | { |
465 | const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution); |
466 | testsTurn.release(); |
467 | |
468 | // TEST 1: thread can't acquire lock |
469 | threadsTurn.acquire(); |
470 | QVERIFY(!normalMutex.try_lock()); |
471 | testsTurn.release(); |
472 | |
473 | // TEST 2: thread can acquire lock |
474 | threadsTurn.acquire(); |
475 | QVERIFY(normalMutex.try_lock()); |
476 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
477 | QVERIFY(!normalMutex.try_lock()); |
478 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
479 | normalMutex.unlock(); |
480 | testsTurn.release(); |
481 | |
482 | // TEST 3: thread can't acquire lock, timeout = waitTime |
483 | threadsTurn.acquire(); |
484 | auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
485 | QVERIFY(!normalMutex.try_lock_until(endTimePoint)); |
486 | QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration); |
487 | testsTurn.release(); |
488 | |
489 | // TEST 4: thread can acquire lock, timeout = waitTime |
490 | threadsTurn.acquire(); |
491 | endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
492 | QVERIFY(normalMutex.try_lock_until(endTimePoint)); |
493 | QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration); |
494 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
495 | endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
496 | // it's non-recursive, so the following lock needs to fail |
497 | QVERIFY(!normalMutex.try_lock_until(endTimePoint)); |
498 | QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration); |
499 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
500 | normalMutex.unlock(); |
501 | testsTurn.release(); |
502 | |
503 | // TEST 5: thread can't acquire lock, timeout = 0 |
504 | threadsTurn.acquire(); |
505 | QVERIFY(!normalMutex.try_lock_until(std::chrono::steady_clock::now())); |
506 | testsTurn.release(); |
507 | |
508 | // TEST 6: thread can acquire lock, timeout = 0 |
509 | threadsTurn.acquire(); |
510 | endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
511 | QVERIFY(normalMutex.try_lock_until(std::chrono::steady_clock::now())); |
512 | QVERIFY(std::chrono::steady_clock::now() < endTimePoint + systemTimersResolutionAsDuration); |
513 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
514 | QVERIFY(!normalMutex.try_lock_until(std::chrono::steady_clock::now())); |
515 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
516 | normalMutex.unlock(); |
517 | testsTurn.release(); |
518 | |
519 | // TEST 7 overflow: thread can acquire lock, timeout = 3000 (QTBUG-24795) |
520 | threadsTurn.acquire(); |
521 | endTimePoint = std::chrono::steady_clock::now() + std::chrono::milliseconds(3000); |
522 | QVERIFY(normalMutex.try_lock_until(endTimePoint)); |
523 | QVERIFY(std::chrono::steady_clock::now() < endTimePoint + systemTimersResolutionAsDuration); |
524 | normalMutex.unlock(); |
525 | testsTurn.release(); |
526 | |
527 | threadsTurn.acquire(); |
528 | } |
529 | }; |
530 | |
531 | Thread thread; |
532 | thread.start(); |
533 | |
534 | // TEST 1: thread can't acquire lock |
535 | testsTurn.acquire(); |
536 | normalMutex.lock(); |
537 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
538 | threadsTurn.release(); |
539 | |
540 | // TEST 2: thread can acquire lock |
541 | testsTurn.acquire(); |
542 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
543 | normalMutex.unlock(); |
544 | threadsTurn.release(); |
545 | |
546 | // TEST 3: thread can't acquire lock, timeout = waitTime |
547 | testsTurn.acquire(); |
548 | normalMutex.lock(); |
549 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
550 | threadsTurn.release(); |
551 | |
552 | // TEST 4: thread can acquire lock, timeout = waitTime |
553 | testsTurn.acquire(); |
554 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
555 | normalMutex.unlock(); |
556 | threadsTurn.release(); |
557 | |
558 | // TEST 5: thread can't acquire lock, timeout = 0 |
559 | testsTurn.acquire(); |
560 | normalMutex.lock(); |
561 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
562 | threadsTurn.release(); |
563 | |
564 | // TEST 6: thread can acquire lock, timeout = 0 |
565 | testsTurn.acquire(); |
566 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
567 | normalMutex.unlock(); |
568 | threadsTurn.release(); |
569 | |
570 | // TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795) |
571 | testsTurn.acquire(); |
572 | normalMutex.lock(); |
573 | threadsTurn.release(); |
574 | QThread::msleep(100); |
575 | normalMutex.unlock(); |
576 | |
577 | // wait for thread to finish |
578 | testsTurn.acquire(); |
579 | threadsTurn.release(); |
580 | thread.wait(); |
581 | #endif |
582 | } |
583 | |
584 | void tst_QMutex::tryLock_recursive() |
585 | { |
586 | class Thread : public QThread |
587 | { |
588 | public: |
589 | void run() |
590 | { |
591 | testsTurn.release(); |
592 | |
593 | threadsTurn.acquire(); |
594 | QVERIFY(!recursiveMutex.tryLock()); |
595 | testsTurn.release(); |
596 | |
597 | threadsTurn.acquire(); |
598 | QVERIFY(recursiveMutex.tryLock()); |
599 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
600 | QVERIFY(recursiveMutex.tryLock()); |
601 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
602 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
603 | recursiveMutex.unlock(); |
604 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
605 | recursiveMutex.unlock(); |
606 | testsTurn.release(); |
607 | |
608 | threadsTurn.acquire(); |
609 | QElapsedTimer timer; |
610 | timer.start(); |
611 | QVERIFY(!recursiveMutex.tryLock(waitTime)); |
612 | QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); |
613 | QVERIFY(!recursiveMutex.tryLock(0)); |
614 | testsTurn.release(); |
615 | |
616 | threadsTurn.acquire(); |
617 | timer.start(); |
618 | QVERIFY(recursiveMutex.tryLock(waitTime)); |
619 | QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution); |
620 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
621 | QVERIFY(recursiveMutex.tryLock(waitTime)); |
622 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
623 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
624 | recursiveMutex.unlock(); |
625 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
626 | recursiveMutex.unlock(); |
627 | testsTurn.release(); |
628 | |
629 | threadsTurn.acquire(); |
630 | QVERIFY(!recursiveMutex.tryLock(0)); |
631 | QVERIFY(!recursiveMutex.tryLock(0)); |
632 | testsTurn.release(); |
633 | |
634 | threadsTurn.acquire(); |
635 | timer.start(); |
636 | QVERIFY(recursiveMutex.tryLock(0)); |
637 | QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); |
638 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
639 | QVERIFY(recursiveMutex.tryLock(0)); |
640 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
641 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
642 | recursiveMutex.unlock(); |
643 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
644 | recursiveMutex.unlock(); |
645 | testsTurn.release(); |
646 | |
647 | threadsTurn.acquire(); |
648 | } |
649 | }; |
650 | |
651 | Thread thread; |
652 | thread.start(); |
653 | |
654 | // thread can't acquire lock |
655 | testsTurn.acquire(); |
656 | recursiveMutex.lock(); |
657 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
658 | recursiveMutex.lock(); |
659 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
660 | threadsTurn.release(); |
661 | |
662 | // thread can acquire lock |
663 | testsTurn.acquire(); |
664 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
665 | recursiveMutex.unlock(); |
666 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
667 | recursiveMutex.unlock(); |
668 | threadsTurn.release(); |
669 | |
670 | // thread can't acquire lock, timeout = waitTime |
671 | testsTurn.acquire(); |
672 | recursiveMutex.lock(); |
673 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
674 | recursiveMutex.lock(); |
675 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
676 | threadsTurn.release(); |
677 | |
678 | // thread can acquire lock, timeout = waitTime |
679 | testsTurn.acquire(); |
680 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
681 | recursiveMutex.unlock(); |
682 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
683 | recursiveMutex.unlock(); |
684 | threadsTurn.release(); |
685 | |
686 | // thread can't acquire lock, timeout = 0 |
687 | testsTurn.acquire(); |
688 | recursiveMutex.lock(); |
689 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
690 | recursiveMutex.lock(); |
691 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
692 | threadsTurn.release(); |
693 | |
694 | // thread can acquire lock, timeout = 0 |
695 | testsTurn.acquire(); |
696 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
697 | recursiveMutex.unlock(); |
698 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
699 | recursiveMutex.unlock(); |
700 | threadsTurn.release(); |
701 | |
702 | // stop thread |
703 | testsTurn.acquire(); |
704 | threadsTurn.release(); |
705 | thread.wait(); |
706 | } |
707 | |
708 | void tst_QMutex::try_lock_for_recursive() |
709 | { |
710 | #if !__has_include(<chrono>) |
711 | QSKIP("This test requires <chrono>" ); |
712 | #else |
713 | class Thread : public QThread |
714 | { |
715 | public: |
716 | void run() |
717 | { |
718 | const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution); |
719 | testsTurn.release(); |
720 | |
721 | threadsTurn.acquire(); |
722 | QVERIFY(!recursiveMutex.try_lock()); |
723 | testsTurn.release(); |
724 | |
725 | threadsTurn.acquire(); |
726 | QVERIFY(recursiveMutex.try_lock()); |
727 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
728 | QVERIFY(recursiveMutex.try_lock()); |
729 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
730 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
731 | recursiveMutex.unlock(); |
732 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
733 | recursiveMutex.unlock(); |
734 | testsTurn.release(); |
735 | |
736 | threadsTurn.acquire(); |
737 | QElapsedTimer timer; |
738 | timer.start(); |
739 | QVERIFY(!recursiveMutex.try_lock_for(waitTimeAsDuration)); |
740 | QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution); |
741 | QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero())); |
742 | testsTurn.release(); |
743 | |
744 | threadsTurn.acquire(); |
745 | timer.start(); |
746 | QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration)); |
747 | QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution); |
748 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
749 | QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration)); |
750 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
751 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
752 | recursiveMutex.unlock(); |
753 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
754 | recursiveMutex.unlock(); |
755 | testsTurn.release(); |
756 | |
757 | threadsTurn.acquire(); |
758 | QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero())); |
759 | QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero())); |
760 | testsTurn.release(); |
761 | |
762 | threadsTurn.acquire(); |
763 | timer.start(); |
764 | QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero())); |
765 | QVERIFY(timer.elapsed() < waitTime + systemTimersResolution); |
766 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
767 | QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero())); |
768 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
769 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
770 | recursiveMutex.unlock(); |
771 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
772 | recursiveMutex.unlock(); |
773 | testsTurn.release(); |
774 | |
775 | threadsTurn.acquire(); |
776 | } |
777 | }; |
778 | |
779 | Thread thread; |
780 | thread.start(); |
781 | |
782 | // thread can't acquire lock |
783 | testsTurn.acquire(); |
784 | recursiveMutex.lock(); |
785 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
786 | recursiveMutex.lock(); |
787 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
788 | threadsTurn.release(); |
789 | |
790 | // thread can acquire lock |
791 | testsTurn.acquire(); |
792 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
793 | recursiveMutex.unlock(); |
794 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
795 | recursiveMutex.unlock(); |
796 | threadsTurn.release(); |
797 | |
798 | // thread can't acquire lock, timeout = waitTime |
799 | testsTurn.acquire(); |
800 | recursiveMutex.lock(); |
801 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
802 | recursiveMutex.lock(); |
803 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
804 | threadsTurn.release(); |
805 | |
806 | // thread can acquire lock, timeout = waitTime |
807 | testsTurn.acquire(); |
808 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
809 | recursiveMutex.unlock(); |
810 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
811 | recursiveMutex.unlock(); |
812 | threadsTurn.release(); |
813 | |
814 | // thread can't acquire lock, timeout = 0 |
815 | testsTurn.acquire(); |
816 | recursiveMutex.lock(); |
817 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
818 | recursiveMutex.lock(); |
819 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
820 | threadsTurn.release(); |
821 | |
822 | // thread can acquire lock, timeout = 0 |
823 | testsTurn.acquire(); |
824 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
825 | recursiveMutex.unlock(); |
826 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
827 | recursiveMutex.unlock(); |
828 | threadsTurn.release(); |
829 | |
830 | // stop thread |
831 | testsTurn.acquire(); |
832 | threadsTurn.release(); |
833 | thread.wait(); |
834 | #endif |
835 | } |
836 | |
837 | void tst_QMutex::try_lock_until_recursive() |
838 | { |
839 | #if !__has_include(<chrono>) |
840 | QSKIP("This test requires <chrono>" ); |
841 | #else |
842 | class Thread : public QThread |
843 | { |
844 | public: |
845 | void run() |
846 | { |
847 | const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution); |
848 | testsTurn.release(); |
849 | |
850 | threadsTurn.acquire(); |
851 | QVERIFY(!recursiveMutex.try_lock()); |
852 | testsTurn.release(); |
853 | |
854 | threadsTurn.acquire(); |
855 | QVERIFY(recursiveMutex.try_lock()); |
856 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
857 | QVERIFY(recursiveMutex.try_lock()); |
858 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
859 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
860 | recursiveMutex.unlock(); |
861 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
862 | recursiveMutex.unlock(); |
863 | testsTurn.release(); |
864 | |
865 | threadsTurn.acquire(); |
866 | auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
867 | QVERIFY(!recursiveMutex.try_lock_until(endTimePoint)); |
868 | QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration); |
869 | QVERIFY(!recursiveMutex.try_lock()); |
870 | testsTurn.release(); |
871 | |
872 | threadsTurn.acquire(); |
873 | endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
874 | QVERIFY(recursiveMutex.try_lock_until(endTimePoint)); |
875 | QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration); |
876 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
877 | endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
878 | QVERIFY(recursiveMutex.try_lock_until(endTimePoint)); |
879 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
880 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
881 | recursiveMutex.unlock(); |
882 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
883 | recursiveMutex.unlock(); |
884 | testsTurn.release(); |
885 | |
886 | threadsTurn.acquire(); |
887 | QVERIFY(!recursiveMutex.try_lock_until(std::chrono::steady_clock::now())); |
888 | QVERIFY(!recursiveMutex.try_lock_until(std::chrono::steady_clock::now())); |
889 | testsTurn.release(); |
890 | |
891 | threadsTurn.acquire(); |
892 | endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration; |
893 | QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now())); |
894 | QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration); |
895 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
896 | QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now())); |
897 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
898 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
899 | recursiveMutex.unlock(); |
900 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
901 | recursiveMutex.unlock(); |
902 | testsTurn.release(); |
903 | |
904 | threadsTurn.acquire(); |
905 | } |
906 | }; |
907 | |
908 | Thread thread; |
909 | thread.start(); |
910 | |
911 | // thread can't acquire lock |
912 | testsTurn.acquire(); |
913 | recursiveMutex.lock(); |
914 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
915 | recursiveMutex.lock(); |
916 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
917 | threadsTurn.release(); |
918 | |
919 | // thread can acquire lock |
920 | testsTurn.acquire(); |
921 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
922 | recursiveMutex.unlock(); |
923 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
924 | recursiveMutex.unlock(); |
925 | threadsTurn.release(); |
926 | |
927 | // thread can't acquire lock, timeout = waitTime |
928 | testsTurn.acquire(); |
929 | recursiveMutex.lock(); |
930 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
931 | recursiveMutex.lock(); |
932 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
933 | threadsTurn.release(); |
934 | |
935 | // thread can acquire lock, timeout = waitTime |
936 | testsTurn.acquire(); |
937 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
938 | recursiveMutex.unlock(); |
939 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
940 | recursiveMutex.unlock(); |
941 | threadsTurn.release(); |
942 | |
943 | // thread can't acquire lock, timeout = 0 |
944 | testsTurn.acquire(); |
945 | recursiveMutex.lock(); |
946 | QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
947 | recursiveMutex.lock(); |
948 | QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
949 | threadsTurn.release(); |
950 | |
951 | // thread can acquire lock, timeout = 0 |
952 | testsTurn.acquire(); |
953 | QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
954 | recursiveMutex.unlock(); |
955 | QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
956 | recursiveMutex.unlock(); |
957 | threadsTurn.release(); |
958 | |
959 | // stop thread |
960 | testsTurn.acquire(); |
961 | threadsTurn.release(); |
962 | thread.wait(); |
963 | #endif |
964 | } |
965 | |
966 | class mutex_Thread : public QThread |
967 | { |
968 | public: |
969 | QMutex mutex; |
970 | QWaitCondition cond; |
971 | |
972 | QMutex &test_mutex; |
973 | |
974 | inline mutex_Thread(QMutex &m) : test_mutex(m) { } |
975 | |
976 | void run() |
977 | { |
978 | test_mutex.lock(); |
979 | |
980 | mutex.lock(); |
981 | for (int i = 0; i < iterations; ++i) { |
982 | cond.wakeOne(); |
983 | cond.wait(lockedMutex: &mutex); |
984 | } |
985 | mutex.unlock(); |
986 | |
987 | test_mutex.unlock(); |
988 | } |
989 | }; |
990 | |
991 | class rmutex_Thread : public QThread |
992 | { |
993 | public: |
994 | QMutex mutex; |
995 | QWaitCondition cond; |
996 | |
997 | QRecursiveMutex &test_mutex; |
998 | |
999 | inline rmutex_Thread(QRecursiveMutex &m) : test_mutex(m) { } |
1000 | |
1001 | void run() |
1002 | { |
1003 | test_mutex.lock(); |
1004 | test_mutex.lock(); |
1005 | test_mutex.lock(); |
1006 | test_mutex.lock(); |
1007 | |
1008 | mutex.lock(); |
1009 | for (int i = 0; i < iterations; ++i) { |
1010 | cond.wakeOne(); |
1011 | cond.wait(lockedMutex: &mutex); |
1012 | } |
1013 | mutex.unlock(); |
1014 | |
1015 | test_mutex.unlock(); |
1016 | test_mutex.unlock(); |
1017 | test_mutex.unlock(); |
1018 | test_mutex.unlock(); |
1019 | } |
1020 | }; |
1021 | |
1022 | void tst_QMutex::lock_unlock_locked_tryLock() |
1023 | { |
1024 | // normal mutex |
1025 | QMutex mutex; |
1026 | mutex_Thread thread(mutex); |
1027 | |
1028 | QRecursiveMutex rmutex; |
1029 | rmutex_Thread rthread(rmutex); |
1030 | |
1031 | for (int i = 0; i < iterations; ++i) { |
1032 | // normal mutex |
1033 | QVERIFY(mutex.tryLock()); |
1034 | mutex.unlock(); |
1035 | |
1036 | thread.mutex.lock(); |
1037 | thread.start(); |
1038 | |
1039 | for (int j = 0; j < iterations; ++j) { |
1040 | QVERIFY(thread.cond.wait(&thread.mutex, 10000)); |
1041 | QVERIFY(!mutex.tryLock()); |
1042 | |
1043 | thread.cond.wakeOne(); |
1044 | } |
1045 | |
1046 | thread.mutex.unlock(); |
1047 | |
1048 | QVERIFY(thread.wait(10000)); |
1049 | QVERIFY(mutex.tryLock()); |
1050 | |
1051 | mutex.unlock(); |
1052 | |
1053 | // recursive mutex |
1054 | QVERIFY(rmutex.tryLock()); |
1055 | QVERIFY(rmutex.tryLock()); |
1056 | QVERIFY(rmutex.tryLock()); |
1057 | QVERIFY(rmutex.tryLock()); |
1058 | |
1059 | rmutex.unlock(); |
1060 | rmutex.unlock(); |
1061 | rmutex.unlock(); |
1062 | rmutex.unlock(); |
1063 | |
1064 | rthread.mutex.lock(); |
1065 | rthread.start(); |
1066 | |
1067 | for (int k = 0; k < iterations; ++k) { |
1068 | QVERIFY(rthread.cond.wait(&rthread.mutex, 10000)); |
1069 | QVERIFY(!rmutex.tryLock()); |
1070 | |
1071 | rthread.cond.wakeOne(); |
1072 | } |
1073 | |
1074 | rthread.mutex.unlock(); |
1075 | |
1076 | QVERIFY(rthread.wait(10000)); |
1077 | QVERIFY(rmutex.tryLock()); |
1078 | QVERIFY(rmutex.tryLock()); |
1079 | QVERIFY(rmutex.tryLock()); |
1080 | QVERIFY(rmutex.tryLock()); |
1081 | |
1082 | rmutex.unlock(); |
1083 | rmutex.unlock(); |
1084 | rmutex.unlock(); |
1085 | rmutex.unlock(); |
1086 | } |
1087 | } |
1088 | |
1089 | enum { one_minute = 6 * 1000, //not really one minute, but else it is too long. |
1090 | threadCount = 10 }; |
1091 | |
1092 | class StressTestThread : public QThread |
1093 | { |
1094 | QElapsedTimer t; |
1095 | public: |
1096 | static QBasicAtomicInt lockCount; |
1097 | static QBasicAtomicInt sentinel; |
1098 | static QMutex mutex; |
1099 | static int errorCount; |
1100 | void start() |
1101 | { |
1102 | t.start(); |
1103 | QThread::start(); |
1104 | } |
1105 | void run() |
1106 | { |
1107 | while (t.elapsed() < one_minute) { |
1108 | mutex.lock(); |
1109 | if (sentinel.ref()) ++errorCount; |
1110 | if (!sentinel.deref()) ++errorCount; |
1111 | lockCount.ref(); |
1112 | mutex.unlock(); |
1113 | if (mutex.tryLock()) { |
1114 | if (sentinel.ref()) ++errorCount; |
1115 | if (!sentinel.deref()) ++errorCount; |
1116 | lockCount.ref(); |
1117 | mutex.unlock(); |
1118 | } |
1119 | } |
1120 | } |
1121 | }; |
1122 | QMutex StressTestThread::mutex; |
1123 | QBasicAtomicInt StressTestThread::lockCount = Q_BASIC_ATOMIC_INITIALIZER(0); |
1124 | QBasicAtomicInt StressTestThread::sentinel = Q_BASIC_ATOMIC_INITIALIZER(-1); |
1125 | int StressTestThread::errorCount = 0; |
1126 | |
1127 | void tst_QMutex::stressTest() |
1128 | { |
1129 | StressTestThread threads[threadCount]; |
1130 | for (int i = 0; i < threadCount; ++i) |
1131 | threads[i].start(); |
1132 | QVERIFY(threads[0].wait(one_minute + 10000)); |
1133 | for (int i = 1; i < threadCount; ++i) |
1134 | QVERIFY(threads[i].wait(10000)); |
1135 | QCOMPARE(StressTestThread::errorCount, 0); |
1136 | qDebug(msg: "locked %d times" , int(StressTestThread::lockCount.loadRelaxed())); |
1137 | } |
1138 | |
1139 | class TryLockRaceThread : public QThread |
1140 | { |
1141 | public: |
1142 | static QMutex mutex; |
1143 | |
1144 | void run() |
1145 | { |
1146 | QElapsedTimer t; |
1147 | t.start(); |
1148 | do { |
1149 | if (mutex.tryLock()) |
1150 | mutex.unlock(); |
1151 | } while (t.elapsed() < one_minute/2); |
1152 | } |
1153 | }; |
1154 | QMutex TryLockRaceThread::mutex; |
1155 | |
1156 | void tst_QMutex::tryLockRace() |
1157 | { |
1158 | // mutex not in use, should be able to lock it |
1159 | QVERIFY(TryLockRaceThread::mutex.tryLock()); |
1160 | TryLockRaceThread::mutex.unlock(); |
1161 | |
1162 | // try to break tryLock |
1163 | TryLockRaceThread thread[threadCount]; |
1164 | for (int i = 0; i < threadCount; ++i) |
1165 | thread[i].start(); |
1166 | for (int i = 0; i < threadCount; ++i) |
1167 | QVERIFY(thread[i].wait()); |
1168 | |
1169 | // mutex not in use, should be able to lock it |
1170 | QVERIFY(TryLockRaceThread::mutex.tryLock()); |
1171 | TryLockRaceThread::mutex.unlock(); |
1172 | } |
1173 | |
1174 | // The following is a regression test for QTBUG-16115, where QMutex could |
1175 | // deadlock after calling tryLock repeatedly. |
1176 | |
1177 | // Variable that will be protected by the mutex. Volatile so that the |
1178 | // the optimiser doesn't mess with it based on the increment-then-decrement |
1179 | // usage pattern. |
1180 | static volatile int tryLockDeadlockCounter; |
1181 | // Counter for how many times the protected variable has an incorrect value. |
1182 | static int tryLockDeadlockFailureCount = 0; |
1183 | |
1184 | void tst_QMutex::tryLockDeadlock() |
1185 | { |
1186 | //Used to deadlock on unix |
1187 | struct TrylockThread : QThread { |
1188 | TrylockThread(QMutex &mut) : mut(mut) {} |
1189 | QMutex &mut; |
1190 | void run() { |
1191 | for (int i = 0; i < 100000; ++i) { |
1192 | if (mut.tryLock(timeout: 0)) { |
1193 | if ((++tryLockDeadlockCounter) != 1) |
1194 | ++tryLockDeadlockFailureCount; |
1195 | if ((--tryLockDeadlockCounter) != 0) |
1196 | ++tryLockDeadlockFailureCount; |
1197 | mut.unlock(); |
1198 | } |
1199 | } |
1200 | } |
1201 | }; |
1202 | QMutex mut; |
1203 | TrylockThread t1(mut); |
1204 | TrylockThread t2(mut); |
1205 | TrylockThread t3(mut); |
1206 | t1.start(); |
1207 | t2.start(); |
1208 | t3.start(); |
1209 | |
1210 | for (int i = 0; i < 100000; ++i) { |
1211 | mut.lock(); |
1212 | if ((++tryLockDeadlockCounter) != 1) |
1213 | ++tryLockDeadlockFailureCount; |
1214 | if ((--tryLockDeadlockCounter) != 0) |
1215 | ++tryLockDeadlockFailureCount; |
1216 | mut.unlock(); |
1217 | } |
1218 | t1.wait(); |
1219 | t2.wait(); |
1220 | t3.wait(); |
1221 | QCOMPARE(tryLockDeadlockFailureCount, 0); |
1222 | } |
1223 | |
1224 | void tst_QMutex::tryLockNegative_data() |
1225 | { |
1226 | QTest::addColumn<int>(name: "timeout" ); |
1227 | QTest::newRow(dataTag: "-1" ) << -1; |
1228 | QTest::newRow(dataTag: "-2" ) << -2; |
1229 | QTest::newRow(dataTag: "INT_MIN/2" ) << INT_MIN/2; |
1230 | QTest::newRow(dataTag: "INT_MIN" ) << INT_MIN; |
1231 | } |
1232 | |
1233 | void tst_QMutex::tryLockNegative() |
1234 | { |
1235 | // the documentation says tryLock() with a negative number is the same as lock() |
1236 | struct TrylockThread : QThread { |
1237 | TrylockThread(QMutex &mut, int timeout) |
1238 | : mut(mut), timeout(timeout), tryLockResult(-1) |
1239 | {} |
1240 | QMutex &mut; |
1241 | int timeout; |
1242 | int tryLockResult; |
1243 | void run() { |
1244 | tryLockResult = mut.tryLock(timeout); |
1245 | mut.unlock(); |
1246 | } |
1247 | }; |
1248 | |
1249 | QFETCH(int, timeout); |
1250 | |
1251 | QMutex mutex; |
1252 | TrylockThread thr(mutex, timeout); |
1253 | mutex.lock(); |
1254 | thr.start(); |
1255 | |
1256 | // the thread should have stopped in tryLock(), waiting for us to unlock |
1257 | // the mutex. The following test can be falsely positive due to timing: |
1258 | // tryLock may still fail but hasn't failed yet. But it certainly cannot be |
1259 | // a false negative: if wait() returns true, tryLock failed. |
1260 | QVERIFY(!thr.wait(200)); |
1261 | |
1262 | // after we unlock the mutex, the thread should succeed in locking, then |
1263 | // unlock and exit. Do this before more tests to avoid deadlocking due to |
1264 | // ~QThread waiting forever on a thread that won't exit. |
1265 | mutex.unlock(); |
1266 | |
1267 | QVERIFY(thr.wait()); |
1268 | QCOMPARE(thr.tryLockResult, 1); |
1269 | } |
1270 | |
1271 | |
1272 | class MoreStressTestThread : public QThread |
1273 | { |
1274 | QElapsedTimer t; |
1275 | public: |
1276 | static QAtomicInt lockCount; |
1277 | static QAtomicInt sentinel[threadCount]; |
1278 | static QMutex mutex[threadCount]; |
1279 | static QAtomicInt errorCount; |
1280 | void start() |
1281 | { |
1282 | t.start(); |
1283 | QThread::start(); |
1284 | } |
1285 | void run() |
1286 | { |
1287 | quint64 i = 0; |
1288 | while (t.elapsed() < one_minute) { |
1289 | i++; |
1290 | uint nb = (i * 9 + lockCount.loadRelaxed() * 13) % threadCount; |
1291 | QMutexLocker locker(&mutex[nb]); |
1292 | if (sentinel[nb].loadRelaxed()) errorCount.ref(); |
1293 | if (sentinel[nb].fetchAndAddRelaxed(valueToAdd: 5)) errorCount.ref(); |
1294 | if (!sentinel[nb].testAndSetRelaxed(expectedValue: 5, newValue: 0)) errorCount.ref(); |
1295 | if (sentinel[nb].loadRelaxed()) errorCount.ref(); |
1296 | lockCount.ref(); |
1297 | nb = (nb * 17 + i * 5 + lockCount.loadRelaxed() * 3) % threadCount; |
1298 | if (mutex[nb].tryLock()) { |
1299 | if (sentinel[nb].loadRelaxed()) errorCount.ref(); |
1300 | if (sentinel[nb].fetchAndAddRelaxed(valueToAdd: 16)) errorCount.ref(); |
1301 | if (!sentinel[nb].testAndSetRelaxed(expectedValue: 16, newValue: 0)) errorCount.ref(); |
1302 | if (sentinel[nb].loadRelaxed()) errorCount.ref(); |
1303 | lockCount.ref(); |
1304 | mutex[nb].unlock(); |
1305 | } |
1306 | nb = (nb * 15 + i * 47 + lockCount.loadRelaxed() * 31) % threadCount; |
1307 | if (mutex[nb].tryLock(timeout: 2)) { |
1308 | if (sentinel[nb].loadRelaxed()) errorCount.ref(); |
1309 | if (sentinel[nb].fetchAndAddRelaxed(valueToAdd: 53)) errorCount.ref(); |
1310 | if (!sentinel[nb].testAndSetRelaxed(expectedValue: 53, newValue: 0)) errorCount.ref(); |
1311 | if (sentinel[nb].loadRelaxed()) errorCount.ref(); |
1312 | lockCount.ref(); |
1313 | mutex[nb].unlock(); |
1314 | } |
1315 | } |
1316 | } |
1317 | }; |
1318 | QMutex MoreStressTestThread::mutex[threadCount]; |
1319 | QAtomicInt MoreStressTestThread::lockCount; |
1320 | QAtomicInt MoreStressTestThread::sentinel[threadCount]; |
1321 | QAtomicInt MoreStressTestThread::errorCount = 0; |
1322 | |
1323 | void tst_QMutex::moreStress() |
1324 | { |
1325 | MoreStressTestThread threads[threadCount]; |
1326 | for (int i = 0; i < threadCount; ++i) |
1327 | threads[i].start(); |
1328 | QVERIFY(threads[0].wait(one_minute + 10000)); |
1329 | for (int i = 1; i < threadCount; ++i) |
1330 | QVERIFY(threads[i].wait(10000)); |
1331 | qDebug(msg: "locked %d times" , MoreStressTestThread::lockCount.loadRelaxed()); |
1332 | QCOMPARE(MoreStressTestThread::errorCount.loadRelaxed(), 0); |
1333 | } |
1334 | |
1335 | |
1336 | QTEST_MAIN(tst_QMutex) |
1337 | #include "tst_qmutex.moc" |
1338 | |