|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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 }; |
|
71 |
|
72 static const int iterations = 100; |
|
73 |
|
74 tst_QMutex::tst_QMutex() |
|
75 |
|
76 { |
|
77 } |
|
78 |
|
79 tst_QMutex::~tst_QMutex() |
|
80 { |
|
81 } |
|
82 |
|
83 QAtomicInt lockCount(0); |
|
84 QMutex normalMutex, recursiveMutex(QMutex::Recursive); |
|
85 QSemaphore testsTurn; |
|
86 QSemaphore threadsTurn; |
|
87 |
|
88 void tst_QMutex::tryLock() |
|
89 { |
|
90 // test non-recursive mutex |
|
91 { |
|
92 class Thread : public QThread |
|
93 { |
|
94 public: |
|
95 void run() |
|
96 { |
|
97 testsTurn.release(); |
|
98 |
|
99 threadsTurn.acquire(); |
|
100 QVERIFY(!normalMutex.tryLock()); |
|
101 testsTurn.release(); |
|
102 |
|
103 threadsTurn.acquire(); |
|
104 QVERIFY(normalMutex.tryLock()); |
|
105 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
106 QVERIFY(!normalMutex.tryLock()); |
|
107 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
108 normalMutex.unlock(); |
|
109 testsTurn.release(); |
|
110 |
|
111 threadsTurn.acquire(); |
|
112 QTime timer; |
|
113 timer.start(); |
|
114 QVERIFY(!normalMutex.tryLock(1000)); |
|
115 QVERIFY(timer.elapsed() >= 1000); |
|
116 testsTurn.release(); |
|
117 |
|
118 threadsTurn.acquire(); |
|
119 timer.start(); |
|
120 QVERIFY(normalMutex.tryLock(1000)); |
|
121 QVERIFY(timer.elapsed() <= 1000); |
|
122 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
123 timer.start(); |
|
124 QVERIFY(!normalMutex.tryLock(1000)); |
|
125 QVERIFY(timer.elapsed() >= 1000); |
|
126 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
127 normalMutex.unlock(); |
|
128 testsTurn.release(); |
|
129 |
|
130 threadsTurn.acquire(); |
|
131 } |
|
132 }; |
|
133 |
|
134 Thread thread; |
|
135 thread.start(); |
|
136 |
|
137 testsTurn.acquire(); |
|
138 normalMutex.lock(); |
|
139 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
140 threadsTurn.release(); |
|
141 |
|
142 testsTurn.acquire(); |
|
143 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
144 normalMutex.unlock(); |
|
145 threadsTurn.release(); |
|
146 |
|
147 testsTurn.acquire(); |
|
148 normalMutex.lock(); |
|
149 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
150 threadsTurn.release(); |
|
151 |
|
152 testsTurn.acquire(); |
|
153 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
154 normalMutex.unlock(); |
|
155 threadsTurn.release(); |
|
156 |
|
157 // wait for thread to finish |
|
158 testsTurn.acquire(); |
|
159 threadsTurn.release(); |
|
160 thread.wait(); |
|
161 } |
|
162 |
|
163 // test recursive mutex |
|
164 { |
|
165 class Thread : public QThread |
|
166 { |
|
167 public: |
|
168 void run() |
|
169 { |
|
170 testsTurn.release(); |
|
171 |
|
172 threadsTurn.acquire(); |
|
173 QVERIFY(!recursiveMutex.tryLock()); |
|
174 testsTurn.release(); |
|
175 |
|
176 threadsTurn.acquire(); |
|
177 QVERIFY(recursiveMutex.tryLock()); |
|
178 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
179 QVERIFY(recursiveMutex.tryLock()); |
|
180 QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
|
181 QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
|
182 recursiveMutex.unlock(); |
|
183 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
184 recursiveMutex.unlock(); |
|
185 testsTurn.release(); |
|
186 |
|
187 threadsTurn.acquire(); |
|
188 QTime timer; |
|
189 timer.start(); |
|
190 QVERIFY(!recursiveMutex.tryLock(1000)); |
|
191 QVERIFY(timer.elapsed() >= 1000); |
|
192 testsTurn.release(); |
|
193 |
|
194 threadsTurn.acquire(); |
|
195 timer.start(); |
|
196 QVERIFY(recursiveMutex.tryLock(1000)); |
|
197 QVERIFY(timer.elapsed() <= 1000); |
|
198 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
199 QVERIFY(recursiveMutex.tryLock(1000)); |
|
200 QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
|
201 QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
|
202 recursiveMutex.unlock(); |
|
203 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
204 recursiveMutex.unlock(); |
|
205 testsTurn.release(); |
|
206 |
|
207 threadsTurn.acquire(); |
|
208 } |
|
209 }; |
|
210 |
|
211 Thread thread; |
|
212 thread.start(); |
|
213 |
|
214 testsTurn.acquire(); |
|
215 recursiveMutex.lock(); |
|
216 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
217 recursiveMutex.lock(); |
|
218 QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
|
219 threadsTurn.release(); |
|
220 |
|
221 testsTurn.acquire(); |
|
222 QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
|
223 recursiveMutex.unlock(); |
|
224 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
225 recursiveMutex.unlock(); |
|
226 threadsTurn.release(); |
|
227 |
|
228 testsTurn.acquire(); |
|
229 recursiveMutex.lock(); |
|
230 QVERIFY(lockCount.testAndSetRelaxed(0, 1)); |
|
231 recursiveMutex.lock(); |
|
232 QVERIFY(lockCount.testAndSetRelaxed(1, 2)); |
|
233 threadsTurn.release(); |
|
234 |
|
235 testsTurn.acquire(); |
|
236 QVERIFY(lockCount.testAndSetRelaxed(2, 1)); |
|
237 recursiveMutex.unlock(); |
|
238 QVERIFY(lockCount.testAndSetRelaxed(1, 0)); |
|
239 recursiveMutex.unlock(); |
|
240 threadsTurn.release(); |
|
241 |
|
242 // stop thread |
|
243 testsTurn.acquire(); |
|
244 threadsTurn.release(); |
|
245 thread.wait(); |
|
246 } |
|
247 } |
|
248 |
|
249 class mutex_Thread : public QThread |
|
250 { |
|
251 public: |
|
252 QMutex mutex; |
|
253 QWaitCondition cond; |
|
254 |
|
255 QMutex &test_mutex; |
|
256 |
|
257 inline mutex_Thread(QMutex &m) : test_mutex(m) { } |
|
258 |
|
259 void run() |
|
260 { |
|
261 test_mutex.lock(); |
|
262 |
|
263 mutex.lock(); |
|
264 for (int i = 0; i < iterations; ++i) { |
|
265 cond.wakeOne(); |
|
266 cond.wait(&mutex); |
|
267 } |
|
268 mutex.unlock(); |
|
269 |
|
270 test_mutex.unlock(); |
|
271 } |
|
272 }; |
|
273 |
|
274 class rmutex_Thread : public QThread |
|
275 { |
|
276 public: |
|
277 QMutex mutex; |
|
278 QWaitCondition cond; |
|
279 |
|
280 QMutex &test_mutex; |
|
281 |
|
282 inline rmutex_Thread(QMutex &m) : test_mutex(m) { } |
|
283 |
|
284 void run() |
|
285 { |
|
286 test_mutex.lock(); |
|
287 test_mutex.lock(); |
|
288 test_mutex.lock(); |
|
289 test_mutex.lock(); |
|
290 |
|
291 mutex.lock(); |
|
292 for (int i = 0; i < iterations; ++i) { |
|
293 cond.wakeOne(); |
|
294 cond.wait(&mutex); |
|
295 } |
|
296 mutex.unlock(); |
|
297 |
|
298 test_mutex.unlock(); |
|
299 test_mutex.unlock(); |
|
300 test_mutex.unlock(); |
|
301 test_mutex.unlock(); |
|
302 } |
|
303 }; |
|
304 |
|
305 #ifdef QT3_SUPPORT |
|
306 #define VERIFY_LOCKED(x) QVERIFY((x).locked()) |
|
307 #define VERIFY_NLOCKED(x) QVERIFY(!(x).locked()) |
|
308 #else |
|
309 #define VERIFY_LOCKED(x) |
|
310 #define VERIFY_NLOCKED(x) |
|
311 #endif // QT3_SUPPORT |
|
312 |
|
313 void tst_QMutex::lock_unlock_locked_tryLock() |
|
314 { |
|
315 // normal mutex |
|
316 QMutex mutex; |
|
317 mutex_Thread thread(mutex); |
|
318 |
|
319 QMutex rmutex(QMutex::Recursive); |
|
320 rmutex_Thread rthread(rmutex); |
|
321 |
|
322 for (int i = 0; i < iterations; ++i) { |
|
323 // normal mutex |
|
324 VERIFY_NLOCKED(mutex); |
|
325 QVERIFY(mutex.tryLock()); |
|
326 mutex.unlock(); |
|
327 |
|
328 thread.mutex.lock(); |
|
329 thread.start(); |
|
330 |
|
331 for (int j = 0; j < iterations; ++j) { |
|
332 QVERIFY(thread.cond.wait(&thread.mutex, 10000)); |
|
333 VERIFY_LOCKED(mutex); |
|
334 QVERIFY(!mutex.tryLock()); |
|
335 |
|
336 thread.cond.wakeOne(); |
|
337 } |
|
338 |
|
339 thread.mutex.unlock(); |
|
340 |
|
341 QVERIFY(thread.wait(10000)); |
|
342 VERIFY_NLOCKED(mutex); |
|
343 QVERIFY(mutex.tryLock()); |
|
344 |
|
345 mutex.unlock(); |
|
346 |
|
347 // recursive mutex |
|
348 VERIFY_NLOCKED(rmutex); |
|
349 QVERIFY(rmutex.tryLock()); |
|
350 QVERIFY(rmutex.tryLock()); |
|
351 QVERIFY(rmutex.tryLock()); |
|
352 QVERIFY(rmutex.tryLock()); |
|
353 |
|
354 rmutex.unlock(); |
|
355 rmutex.unlock(); |
|
356 rmutex.unlock(); |
|
357 rmutex.unlock(); |
|
358 |
|
359 rthread.mutex.lock(); |
|
360 rthread.start(); |
|
361 |
|
362 for (int k = 0; k < iterations; ++k) { |
|
363 QVERIFY(rthread.cond.wait(&rthread.mutex, 10000)); |
|
364 VERIFY_LOCKED(rmutex); |
|
365 QVERIFY(!rmutex.tryLock()); |
|
366 |
|
367 rthread.cond.wakeOne(); |
|
368 } |
|
369 |
|
370 rthread.mutex.unlock(); |
|
371 |
|
372 QVERIFY(rthread.wait(10000)); |
|
373 VERIFY_NLOCKED(rmutex); |
|
374 QVERIFY(rmutex.tryLock()); |
|
375 QVERIFY(rmutex.tryLock()); |
|
376 QVERIFY(rmutex.tryLock()); |
|
377 QVERIFY(rmutex.tryLock()); |
|
378 |
|
379 rmutex.unlock(); |
|
380 rmutex.unlock(); |
|
381 rmutex.unlock(); |
|
382 rmutex.unlock(); |
|
383 } |
|
384 } |
|
385 |
|
386 enum { one_minute = 60 * 1000, threadCount = 10 }; |
|
387 |
|
388 class StressTestThread : public QThread |
|
389 { |
|
390 QTime t; |
|
391 public: |
|
392 static QBasicAtomicInt lockCount; |
|
393 static QBasicAtomicInt sentinel; |
|
394 static QMutex mutex; |
|
395 void start() |
|
396 { |
|
397 t.start(); |
|
398 QThread::start(); |
|
399 } |
|
400 void run() |
|
401 { |
|
402 while (t.elapsed() < one_minute) { |
|
403 mutex.lock(); |
|
404 Q_ASSERT(!sentinel.ref()); |
|
405 Q_ASSERT(sentinel.deref()); |
|
406 lockCount.ref(); |
|
407 mutex.unlock(); |
|
408 if (mutex.tryLock()) { |
|
409 Q_ASSERT(!sentinel.ref()); |
|
410 Q_ASSERT(sentinel.deref()); |
|
411 lockCount.ref(); |
|
412 mutex.unlock(); |
|
413 } |
|
414 } |
|
415 } |
|
416 }; |
|
417 QMutex StressTestThread::mutex; |
|
418 QBasicAtomicInt StressTestThread::lockCount = Q_BASIC_ATOMIC_INITIALIZER(0); |
|
419 QBasicAtomicInt StressTestThread::sentinel = Q_BASIC_ATOMIC_INITIALIZER(-1); |
|
420 |
|
421 void tst_QMutex::stressTest() |
|
422 { |
|
423 StressTestThread threads[threadCount]; |
|
424 for (int i = 0; i < threadCount; ++i) |
|
425 threads[i].start(); |
|
426 QVERIFY(threads[0].wait(one_minute + 10000)); |
|
427 for (int i = 1; i < threadCount; ++i) |
|
428 QVERIFY(threads[i].wait(10000)); |
|
429 qDebug("locked %d times", int(StressTestThread::lockCount)); |
|
430 } |
|
431 |
|
432 class TryLockRaceThread : public QThread |
|
433 { |
|
434 public: |
|
435 static QMutex mutex; |
|
436 |
|
437 void run() |
|
438 { |
|
439 QTime t; |
|
440 t.start(); |
|
441 do { |
|
442 if (mutex.tryLock()) |
|
443 mutex.unlock(); |
|
444 } while (t.elapsed() < 20000); |
|
445 } |
|
446 }; |
|
447 QMutex TryLockRaceThread::mutex; |
|
448 |
|
449 void tst_QMutex::tryLockRace() |
|
450 { |
|
451 // mutex not in use, should be able to lock it |
|
452 QVERIFY(TryLockRaceThread::mutex.tryLock()); |
|
453 TryLockRaceThread::mutex.unlock(); |
|
454 |
|
455 // try to break tryLock |
|
456 TryLockRaceThread thread[threadCount]; |
|
457 for (int i = 0; i < threadCount; ++i) |
|
458 thread[i].start(); |
|
459 for (int i = 0; i < threadCount; ++i) |
|
460 QVERIFY(thread[i].wait()); |
|
461 |
|
462 // mutex not in use, should be able to lock it |
|
463 QVERIFY(TryLockRaceThread::mutex.tryLock()); |
|
464 TryLockRaceThread::mutex.unlock(); |
|
465 } |
|
466 |
|
467 QTEST_MAIN(tst_QMutex) |
|
468 #include "tst_qmutex.moc" |