Fix QMutex can deadlock when calling tryLock
[qt:qt.git] / tests / auto / qmutex / tst_qmutex.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45 #include <qatomic.h>
46 #include <qcoreapplication.h>
47 #include <qdatetime.h>
48 #include <qmutex.h>
49 #include <qthread.h>
50 #include <qwaitcondition.h>
51
52
53
54 //TESTED_CLASS=
55 //TESTED_FILES=
56
57 class tst_QMutex : public QObject
58 {
59     Q_OBJECT
60
61 public:
62     tst_QMutex();
63     virtual ~tst_QMutex();
64
65 private slots:
66     void tryLock();
67     void lock_unlock_locked_tryLock();
68     void stressTest();
69     void tryLockRace();
70     void qtbug16115_trylock();
71 };
72
73 static const int iterations = 100;
74
75 tst_QMutex::tst_QMutex()
76
77 {
78 }
79
80 tst_QMutex::~tst_QMutex()
81 {
82 }
83
84 QAtomicInt lockCount(0);
85 QMutex normalMutex, recursiveMutex(QMutex::Recursive);
86 QSemaphore testsTurn;
87 QSemaphore threadsTurn;
88
89 void tst_QMutex::tryLock()
90 {
91     // test non-recursive mutex
92     {
93         class Thread : public QThread
94         {
95         public:
96             void run()
97             {
98                 testsTurn.release();
99
100                 threadsTurn.acquire();
101                 QVERIFY(!normalMutex.tryLock());
102                 testsTurn.release();
103
104                 threadsTurn.acquire();
105                 QVERIFY(normalMutex.tryLock());
106                 QVERIFY(lockCount.testAndSetRelaxed(0, 1));
107                 QVERIFY(!normalMutex.tryLock());
108                 QVERIFY(lockCount.testAndSetRelaxed(1, 0));
109                 normalMutex.unlock();
110                 testsTurn.release();
111
112                 threadsTurn.acquire();
113                 QTime timer;
114                 timer.start();
115                 QVERIFY(!normalMutex.tryLock(1000));
116                 QVERIFY(timer.elapsed() >= 1000);
117                 testsTurn.release();
118
119                 threadsTurn.acquire();
120                 timer.start();
121                 QVERIFY(normalMutex.tryLock(1000));
122                 QVERIFY(timer.elapsed() <= 1000);
123                 QVERIFY(lockCount.testAndSetRelaxed(0, 1));
124                 timer.start();
125                 QVERIFY(!normalMutex.tryLock(1000));
126                 QVERIFY(timer.elapsed() >= 1000);
127                 QVERIFY(lockCount.testAndSetRelaxed(1, 0));
128                 normalMutex.unlock();
129                 testsTurn.release();
130
131                 threadsTurn.acquire();
132             }
133         };
134
135         Thread thread;
136         thread.start();
137
138         testsTurn.acquire();
139         normalMutex.lock();
140         QVERIFY(lockCount.testAndSetRelaxed(0, 1));
141         threadsTurn.release();
142
143         testsTurn.acquire();
144         QVERIFY(lockCount.testAndSetRelaxed(1, 0));
145         normalMutex.unlock();
146         threadsTurn.release();
147
148         testsTurn.acquire();
149         normalMutex.lock();
150         QVERIFY(lockCount.testAndSetRelaxed(0, 1));
151         threadsTurn.release();
152
153         testsTurn.acquire();
154         QVERIFY(lockCount.testAndSetRelaxed(1, 0));
155         normalMutex.unlock();
156         threadsTurn.release();
157
158         // wait for thread to finish
159         testsTurn.acquire();
160         threadsTurn.release();
161         thread.wait();
162     }
163
164     // test recursive mutex
165     {
166         class Thread : public QThread
167         {
168         public:
169             void run()
170             {
171                 testsTurn.release();
172
173                 threadsTurn.acquire();
174                 QVERIFY(!recursiveMutex.tryLock());
175                 testsTurn.release();
176
177                 threadsTurn.acquire();
178                 QVERIFY(recursiveMutex.tryLock());
179                 QVERIFY(lockCount.testAndSetRelaxed(0, 1));
180                 QVERIFY(recursiveMutex.tryLock());
181                 QVERIFY(lockCount.testAndSetRelaxed(1, 2));
182                 QVERIFY(lockCount.testAndSetRelaxed(2, 1));
183                 recursiveMutex.unlock();
184                 QVERIFY(lockCount.testAndSetRelaxed(1, 0));
185                 recursiveMutex.unlock();
186                 testsTurn.release();
187
188                 threadsTurn.acquire();
189                 QTime timer;
190                 timer.start();
191                 QVERIFY(!recursiveMutex.tryLock(1000));
192                 QVERIFY(timer.elapsed() >= 1000);
193                 testsTurn.release();
194
195                 threadsTurn.acquire();
196                 timer.start();
197                 QVERIFY(recursiveMutex.tryLock(1000));
198                 QVERIFY(timer.elapsed() <= 1000);
199                 QVERIFY(lockCount.testAndSetRelaxed(0, 1));
200                 QVERIFY(recursiveMutex.tryLock(1000));
201                 QVERIFY(lockCount.testAndSetRelaxed(1, 2));
202                 QVERIFY(lockCount.testAndSetRelaxed(2, 1));
203                 recursiveMutex.unlock();
204                 QVERIFY(lockCount.testAndSetRelaxed(1, 0));
205                 recursiveMutex.unlock();
206                 testsTurn.release();
207
208                 threadsTurn.acquire();
209             }
210         };
211
212         Thread thread;
213         thread.start();
214
215         testsTurn.acquire();
216         recursiveMutex.lock();
217         QVERIFY(lockCount.testAndSetRelaxed(0, 1));
218         recursiveMutex.lock();
219         QVERIFY(lockCount.testAndSetRelaxed(1, 2));
220         threadsTurn.release();
221
222         testsTurn.acquire();
223         QVERIFY(lockCount.testAndSetRelaxed(2, 1));
224         recursiveMutex.unlock();
225         QVERIFY(lockCount.testAndSetRelaxed(1, 0));
226         recursiveMutex.unlock();
227         threadsTurn.release();
228
229         testsTurn.acquire();
230         recursiveMutex.lock();
231         QVERIFY(lockCount.testAndSetRelaxed(0, 1));
232         recursiveMutex.lock();
233         QVERIFY(lockCount.testAndSetRelaxed(1, 2));
234         threadsTurn.release();
235
236         testsTurn.acquire();
237         QVERIFY(lockCount.testAndSetRelaxed(2, 1));
238         recursiveMutex.unlock();
239         QVERIFY(lockCount.testAndSetRelaxed(1, 0));
240         recursiveMutex.unlock();
241         threadsTurn.release();
242
243         // stop thread
244         testsTurn.acquire();
245         threadsTurn.release();
246         thread.wait();
247     }
248 }
249
250 class mutex_Thread : public QThread
251 {
252 public:
253     QMutex mutex;
254     QWaitCondition cond;
255
256     QMutex &test_mutex;
257
258     inline mutex_Thread(QMutex &m) : test_mutex(m) { }
259
260     void run()
261     {
262         test_mutex.lock();
263
264         mutex.lock();
265         for (int i = 0; i < iterations; ++i) {
266             cond.wakeOne();
267             cond.wait(&mutex);
268         }
269         mutex.unlock();
270
271         test_mutex.unlock();
272     }
273 };
274
275 class rmutex_Thread : public QThread
276 {
277 public:
278     QMutex mutex;
279     QWaitCondition cond;
280
281     QMutex &test_mutex;
282
283     inline rmutex_Thread(QMutex &m) : test_mutex(m) { }
284
285     void run()
286     {
287         test_mutex.lock();
288         test_mutex.lock();
289         test_mutex.lock();
290         test_mutex.lock();
291
292         mutex.lock();
293         for (int i = 0; i < iterations; ++i) {
294             cond.wakeOne();
295             cond.wait(&mutex);
296         }
297         mutex.unlock();
298
299         test_mutex.unlock();
300         test_mutex.unlock();
301         test_mutex.unlock();
302         test_mutex.unlock();
303     }
304 };
305
306 #ifdef QT3_SUPPORT
307 #define VERIFY_LOCKED(x) QVERIFY((x).locked())
308 #define VERIFY_NLOCKED(x) QVERIFY(!(x).locked())
309 #else
310 #define VERIFY_LOCKED(x)
311 #define VERIFY_NLOCKED(x)
312 #endif // QT3_SUPPORT
313
314 void tst_QMutex::lock_unlock_locked_tryLock()
315 {
316     // normal mutex
317     QMutex mutex;
318     mutex_Thread thread(mutex);
319
320     QMutex rmutex(QMutex::Recursive);
321     rmutex_Thread rthread(rmutex);
322
323     for (int i = 0; i < iterations; ++i) {
324         // normal mutex
325         VERIFY_NLOCKED(mutex);
326         QVERIFY(mutex.tryLock());
327         mutex.unlock();
328
329         thread.mutex.lock();
330         thread.start();
331
332         for (int j = 0; j < iterations; ++j) {
333             QVERIFY(thread.cond.wait(&thread.mutex, 10000));
334             VERIFY_LOCKED(mutex);
335             QVERIFY(!mutex.tryLock());
336
337             thread.cond.wakeOne();
338         }
339
340         thread.mutex.unlock();
341
342         QVERIFY(thread.wait(10000));
343         VERIFY_NLOCKED(mutex);
344         QVERIFY(mutex.tryLock());
345
346         mutex.unlock();
347
348         // recursive mutex
349         VERIFY_NLOCKED(rmutex);
350         QVERIFY(rmutex.tryLock());
351         QVERIFY(rmutex.tryLock());
352         QVERIFY(rmutex.tryLock());
353         QVERIFY(rmutex.tryLock());
354
355         rmutex.unlock();
356         rmutex.unlock();
357         rmutex.unlock();
358         rmutex.unlock();
359
360         rthread.mutex.lock();
361         rthread.start();
362
363         for (int k = 0; k < iterations; ++k) {
364             QVERIFY(rthread.cond.wait(&rthread.mutex, 10000));
365             VERIFY_LOCKED(rmutex);
366             QVERIFY(!rmutex.tryLock());
367
368             rthread.cond.wakeOne();
369         }
370
371         rthread.mutex.unlock();
372
373         QVERIFY(rthread.wait(10000));
374         VERIFY_NLOCKED(rmutex);
375         QVERIFY(rmutex.tryLock());
376         QVERIFY(rmutex.tryLock());
377         QVERIFY(rmutex.tryLock());
378         QVERIFY(rmutex.tryLock());
379
380         rmutex.unlock();
381         rmutex.unlock();
382         rmutex.unlock();
383         rmutex.unlock();
384     }
385 }
386
387 enum { one_minute = 60 * 1000, threadCount = 10 };
388
389 class StressTestThread : public QThread
390 {
391     QTime t;
392 public:
393     static QBasicAtomicInt lockCount;
394     static QBasicAtomicInt sentinel;
395     static QMutex mutex;
396     void start()
397     {
398         t.start();
399         QThread::start();
400     }
401     void run()
402     {
403         while (t.elapsed() < one_minute) {
404             mutex.lock();
405             Q_ASSERT(!sentinel.ref());
406             Q_ASSERT(sentinel.deref());
407             lockCount.ref();
408             mutex.unlock();
409             if (mutex.tryLock()) {
410                 Q_ASSERT(!sentinel.ref());
411                 Q_ASSERT(sentinel.deref());
412                 lockCount.ref();
413                 mutex.unlock();
414             }
415         }
416     }
417 };
418 QMutex StressTestThread::mutex;
419 QBasicAtomicInt StressTestThread::lockCount = Q_BASIC_ATOMIC_INITIALIZER(0);
420 QBasicAtomicInt StressTestThread::sentinel = Q_BASIC_ATOMIC_INITIALIZER(-1);
421
422 void tst_QMutex::stressTest()
423 {
424     StressTestThread threads[threadCount];
425     for (int i = 0; i < threadCount; ++i)
426         threads[i].start();
427     QVERIFY(threads[0].wait(one_minute + 10000));
428     for (int i = 1; i < threadCount; ++i)
429         QVERIFY(threads[i].wait(10000));
430     qDebug("locked %d times", int(StressTestThread::lockCount));
431 }
432
433 class TryLockRaceThread : public QThread
434 {
435 public:
436     static QMutex mutex;
437
438     void run()
439     {
440         QTime t;
441         t.start();
442         do {
443             if (mutex.tryLock())
444                 mutex.unlock();
445         } while (t.elapsed() < 20000);
446     }
447 };
448 QMutex TryLockRaceThread::mutex;
449
450 void tst_QMutex::tryLockRace()
451 {
452     // mutex not in use, should be able to lock it
453     QVERIFY(TryLockRaceThread::mutex.tryLock());
454     TryLockRaceThread::mutex.unlock();
455
456     // try to break tryLock
457     TryLockRaceThread thread[threadCount];
458     for (int i = 0; i < threadCount; ++i)
459         thread[i].start();
460     for (int i = 0; i < threadCount; ++i)
461         QVERIFY(thread[i].wait());
462
463     // mutex not in use, should be able to lock it
464     QVERIFY(TryLockRaceThread::mutex.tryLock());
465     TryLockRaceThread::mutex.unlock();
466 }
467
468 static volatile int qtbug16115_trylock_counter;
469
470 void tst_QMutex::qtbug16115_trylock()
471 {
472     //Used to deadlock on unix
473     struct TrylockThread : QThread {
474         TrylockThread(QMutex &mut) : mut(mut) {}
475         QMutex &mut;
476         void run() {
477             for (int i = 0; i < 1000000; ++i) {
478                 if (mut.tryLock(0)) {
479                     Q_ASSERT((++qtbug16115_trylock_counter) == 1);
480                     Q_ASSERT((--qtbug16115_trylock_counter) == 0);
481                     mut.unlock();
482                 }
483             }
484         }
485     };
486     QMutex mut;
487     TrylockThread t1(mut);
488     TrylockThread t2(mut);
489     TrylockThread t3(mut);
490     t1.start();
491     t2.start();
492     t3.start();
493
494     for (int i = 0; i < 1000000; ++i) {
495         mut.lock();
496         Q_ASSERT((++qtbug16115_trylock_counter) == 1);
497         Q_ASSERT((--qtbug16115_trylock_counter) == 0);
498         mut.unlock();
499     }
500     t1.wait();
501     t2.wait();
502     t3.wait();
503 }
504
505 QTEST_MAIN(tst_QMutex)
506 #include "tst_qmutex.moc"