1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite 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 The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company 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 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtCore/QtCore>
43 #include <QtTest/QtTest>
44
45 #include <math.h>
46
47 #ifdef Q_OS_SYMBIAN
48 # include <e32std.h>
49 typedef RMutex NativeMutexType;
NativeMutexInitialize(NativeMutexType * mutex)50 void NativeMutexInitialize(NativeMutexType *mutex)
51 {
52 mutex->CreateLocal();
53 }
NativeMutexDestroy(NativeMutexType * mutex)54 void NativeMutexDestroy(NativeMutexType *mutex)
55 {
56 mutex->Close();
57 }
NativeMutexLock(NativeMutexType * mutex)58 void NativeMutexLock(NativeMutexType *mutex)
59 {
60 mutex->Wait();
61 }
NativeMutexUnlock(NativeMutexType * mutex)62 void NativeMutexUnlock(NativeMutexType *mutex)
63 {
64 mutex->Signal();
65 }
66 #elif defined(Q_OS_UNIX)
67 # include <pthread.h>
68 # include <errno.h>
69 typedef pthread_mutex_t NativeMutexType;
NativeMutexInitialize(NativeMutexType * mutex)70 void NativeMutexInitialize(NativeMutexType *mutex)
71 {
72 pthread_mutex_init(mutex, NULL);
73 }
NativeMutexDestroy(NativeMutexType * mutex)74 void NativeMutexDestroy(NativeMutexType *mutex)
75 {
76 pthread_mutex_destroy(mutex);
77 }
NativeMutexLock(NativeMutexType * mutex)78 void NativeMutexLock(NativeMutexType *mutex)
79 {
80 pthread_mutex_lock(mutex);
81 }
NativeMutexUnlock(NativeMutexType * mutex)82 void NativeMutexUnlock(NativeMutexType *mutex)
83 {
84 pthread_mutex_unlock(mutex);
85 }
86 #elif defined(Q_OS_WIN)
87 # define _WIN32_WINNT 0x0400
88 # include <windows.h>
89 typedef CRITICAL_SECTION NativeMutexType;
NativeMutexInitialize(NativeMutexType * mutex)90 void NativeMutexInitialize(NativeMutexType *mutex)
91 {
92 InitializeCriticalSection(mutex);
93 }
NativeMutexDestroy(NativeMutexType * mutex)94 void NativeMutexDestroy(NativeMutexType *mutex)
95 {
96 DeleteCriticalSection(mutex);
97 }
NativeMutexLock(NativeMutexType * mutex)98 void NativeMutexLock(NativeMutexType *mutex)
99 {
100 EnterCriticalSection(mutex);
101 }
NativeMutexUnlock(NativeMutexType * mutex)102 void NativeMutexUnlock(NativeMutexType *mutex)
103 {
104 LeaveCriticalSection(mutex);
105 }
106 #endif
107
108 //TESTED_FILES=
109
110 class tst_QMutex : public QObject
111 {
112 Q_OBJECT
113
114 int threadCount;
115
116 public:
117 // barriers for the contended tests
118 static QSemaphore semaphore1, semaphore2, semaphore3, semaphore4;
119
tst_QMutex()120 tst_QMutex()
121 {
122 // at least 2 threads, even on single cpu/core machines
123 threadCount = qMax(2, QThread::idealThreadCount());
124 qDebug("thread count: %d", threadCount);
125 }
126
127 private slots:
128 void noThread_data();
129 void noThread();
130
131 void constructionNative();
132 void uncontendedNative();
133 void constructionQMutex();
134 void uncontendedQMutex();
135 void uncontendedQMutexLocker();
136
137 void contendedNative_data();
contendedQMutex_data()138 void contendedQMutex_data() { contendedNative_data(); }
contendedQMutexLocker_data()139 void contendedQMutexLocker_data() { contendedNative_data(); }
140
141 void contendedNative();
142 void contendedQMutex();
143 void contendedQMutexLocker();
144 };
145
146 QSemaphore tst_QMutex::semaphore1;
147 QSemaphore tst_QMutex::semaphore2;
148 QSemaphore tst_QMutex::semaphore3;
149 QSemaphore tst_QMutex::semaphore4;
150
noThread_data()151 void tst_QMutex::noThread_data()
152 {
153 QTest::addColumn<int>("t");
154
155 QTest::newRow("noLock") << 1;
156 QTest::newRow("QMutexInline") << 2;
157 QTest::newRow("QMutex") << 3;
158 QTest::newRow("QMutexLocker") << 4;
159 }
160
noThread()161 void tst_QMutex::noThread()
162 {
163 volatile int count = 0;
164 const int N = 5000000;
165 QMutex mtx;
166
167 QFETCH(int, t);
168 switch(t) {
169 case 1:
170 QBENCHMARK {
171 count = 0;
172 for (int i = 0; i < N; i++) {
173 count++;
174 }
175 }
176 break;
177 case 2:
178 QBENCHMARK {
179 count = 0;
180 for (int i = 0; i < N; i++) {
181 mtx.lockInline();
182 count++;
183 mtx.unlockInline();
184 }
185 }
186 break;
187 case 3:
188 QBENCHMARK {
189 count = 0;
190 for (int i = 0; i < N; i++) {
191 mtx.lock();
192 count++;
193 mtx.unlock();
194 }
195 }
196 break;
197 case 4:
198 QBENCHMARK {
199 count = 0;
200 for (int i = 0; i < N; i++) {
201 QMutexLocker locker(&mtx);
202 count++;
203 }
204 }
205 break;
206 }
207 QCOMPARE(int(count), N);
208 }
209
constructionNative()210 void tst_QMutex::constructionNative()
211 {
212 QBENCHMARK {
213 NativeMutexType mutex;
214 NativeMutexInitialize(&mutex);
215 NativeMutexDestroy(&mutex);
216 }
217 }
218
uncontendedNative()219 void tst_QMutex::uncontendedNative()
220 {
221 NativeMutexType mutex;
222 NativeMutexInitialize(&mutex);
223 QBENCHMARK {
224 NativeMutexLock(&mutex);
225 NativeMutexUnlock(&mutex);
226 }
227 NativeMutexDestroy(&mutex);
228 }
229
constructionQMutex()230 void tst_QMutex::constructionQMutex()
231 {
232 QBENCHMARK {
233 QMutex mutex;
234 Q_UNUSED(mutex);
235 }
236 }
237
uncontendedQMutex()238 void tst_QMutex::uncontendedQMutex()
239 {
240 QMutex mutex;
241 QBENCHMARK {
242 mutex.lock();
243 mutex.unlock();
244 }
245 }
246
uncontendedQMutexLocker()247 void tst_QMutex::uncontendedQMutexLocker()
248 {
249 QMutex mutex;
250 QBENCHMARK {
251 QMutexLocker locker(&mutex);
252 }
253 }
254
contendedNative_data()255 void tst_QMutex::contendedNative_data()
256 {
257 QTest::addColumn<int>("iterations");
258 QTest::addColumn<int>("msleepDuration");
259 QTest::addColumn<bool>("use2mutexes");
260
261 QTest::newRow("baseline") << 0 << -1 << false;
262
263 QTest::newRow("no msleep, 1 mutex") << 1000 << -1 << false;
264 QTest::newRow("no msleep, 2 mutexes") << 1000 << -1 << true;
265 QTest::newRow("msleep(0), 1 mutex") << 1000 << 0 << false;
266 QTest::newRow("msleep(0), 2 mutexes") << 1000 << 0 << true;
267 QTest::newRow("msleep(1), 1 mutex") << 10 << 1 << false;
268 QTest::newRow("msleep(1), 2 mutexes") << 10 << 1 << true;
269 QTest::newRow("msleep(2), 1 mutex") << 10 << 2 << false;
270 QTest::newRow("msleep(2), 2 mutexes") << 10 << 2 << true;
271 QTest::newRow("msleep(10), 1 mutex") << 10 << 10 << false;
272 QTest::newRow("msleep(10), 2 mutexes") << 10 << 10 << true;
273 }
274
275 class NativeMutexThread : public QThread
276 {
277 NativeMutexType *mutex1, *mutex2;
278 int iterations, msleepDuration;
279 bool use2mutexes;
280 public:
281 bool done;
NativeMutexThread(NativeMutexType * mutex1,NativeMutexType * mutex2,int iterations,int msleepDuration,bool use2mutexes)282 NativeMutexThread(NativeMutexType *mutex1, NativeMutexType *mutex2, int iterations, int msleepDuration, bool use2mutexes)
283 : mutex1(mutex1), mutex2(mutex2), iterations(iterations), msleepDuration(msleepDuration), use2mutexes(use2mutexes), done(false)
284 { }
run()285 void run() {
286 forever {
287 tst_QMutex::semaphore1.release();
288 tst_QMutex::semaphore2.acquire();
289 if (done)
290 break;
291 for (int i = 0; i < iterations; ++i) {
292 NativeMutexLock(mutex1);
293 if (use2mutexes)
294 NativeMutexLock(mutex2);
295 if (msleepDuration >= 0)
296 msleep(msleepDuration);
297 if (use2mutexes)
298 NativeMutexUnlock(mutex2);
299 NativeMutexUnlock(mutex1);
300
301 QThread::yieldCurrentThread();
302 }
303 tst_QMutex::semaphore3.release();
304 tst_QMutex::semaphore4.acquire();
305 }
306 }
307 };
308
contendedNative()309 void tst_QMutex::contendedNative()
310 {
311 QFETCH(int, iterations);
312 QFETCH(int, msleepDuration);
313 QFETCH(bool, use2mutexes);
314
315 NativeMutexType mutex1, mutex2;
316 NativeMutexInitialize(&mutex1);
317 NativeMutexInitialize(&mutex2);
318
319 QVector<NativeMutexThread *> threads(threadCount);
320 for (int i = 0; i < threads.count(); ++i) {
321 threads[i] = new NativeMutexThread(&mutex1, &mutex2, iterations, msleepDuration, use2mutexes);
322 threads[i]->start();
323 }
324
325 QBENCHMARK {
326 semaphore1.acquire(threadCount);
327 semaphore2.release(threadCount);
328 semaphore3.acquire(threadCount);
329 semaphore4.release(threadCount);
330 }
331
332 for (int i = 0; i < threads.count(); ++i)
333 threads[i]->done = true;
334 semaphore1.acquire(threadCount);
335 semaphore2.release(threadCount);
336 for (int i = 0; i < threads.count(); ++i)
337 threads[i]->wait();
338 qDeleteAll(threads);
339
340 NativeMutexDestroy(&mutex1);
341 NativeMutexDestroy(&mutex2);
342 }
343
344 class QMutexThread : public QThread
345 {
346 QMutex *mutex1, *mutex2;
347 int iterations, msleepDuration;
348 bool use2mutexes;
349 public:
350 bool done;
QMutexThread(QMutex * mutex1,QMutex * mutex2,int iterations,int msleepDuration,bool use2mutexes)351 QMutexThread(QMutex *mutex1, QMutex *mutex2, int iterations, int msleepDuration, bool use2mutexes)
352 : mutex1(mutex1), mutex2(mutex2), iterations(iterations), msleepDuration(msleepDuration), use2mutexes(use2mutexes), done(false)
353 { }
run()354 void run() {
355 forever {
356 tst_QMutex::semaphore1.release();
357 tst_QMutex::semaphore2.acquire();
358 if (done)
359 break;
360 for (int i = 0; i < iterations; ++i) {
361 mutex1->lock();
362 if (use2mutexes)
363 mutex2->lock();
364 if (msleepDuration >= 0)
365 msleep(msleepDuration);
366 if (use2mutexes)
367 mutex2->unlock();
368 mutex1->unlock();
369
370 QThread::yieldCurrentThread();
371 }
372 tst_QMutex::semaphore3.release();
373 tst_QMutex::semaphore4.acquire();
374 }
375 }
376 };
377
contendedQMutex()378 void tst_QMutex::contendedQMutex()
379 {
380 QFETCH(int, iterations);
381 QFETCH(int, msleepDuration);
382 QFETCH(bool, use2mutexes);
383
384 QMutex mutex1, mutex2;
385
386 QVector<QMutexThread *> threads(threadCount);
387 for (int i = 0; i < threads.count(); ++i) {
388 threads[i] = new QMutexThread(&mutex1, &mutex2, iterations, msleepDuration, use2mutexes);
389 threads[i]->start();
390 }
391
392 QBENCHMARK {
393 semaphore1.acquire(threadCount);
394 semaphore2.release(threadCount);
395 semaphore3.acquire(threadCount);
396 semaphore4.release(threadCount);
397 }
398
399 for (int i = 0; i < threads.count(); ++i)
400 threads[i]->done = true;
401 semaphore1.acquire(threadCount);
402 semaphore2.release(threadCount);
403 for (int i = 0; i < threads.count(); ++i)
404 threads[i]->wait();
405 qDeleteAll(threads);
406 }
407
408 class QMutexLockerThread : public QThread
409 {
410 QMutex *mutex1, *mutex2;
411 int iterations, msleepDuration;
412 bool use2mutexes;
413 public:
414 bool done;
QMutexLockerThread(QMutex * mutex1,QMutex * mutex2,int iterations,int msleepDuration,bool use2mutexes)415 QMutexLockerThread(QMutex *mutex1, QMutex *mutex2, int iterations, int msleepDuration, bool use2mutexes)
416 : mutex1(mutex1), mutex2(mutex2), iterations(iterations), msleepDuration(msleepDuration), use2mutexes(use2mutexes), done(false)
417 { }
run()418 void run() {
419 forever {
420 tst_QMutex::semaphore1.release();
421 tst_QMutex::semaphore2.acquire();
422 if (done)
423 break;
424 for (int i = 0; i < iterations; ++i) {
425 {
426 QMutexLocker locker1(mutex1);
427 QMutexLocker locker2(use2mutexes ? mutex2 : 0);
428 if (msleepDuration >= 0)
429 msleep(msleepDuration);
430 }
431
432 QThread::yieldCurrentThread();
433 }
434 tst_QMutex::semaphore3.release();
435 tst_QMutex::semaphore4.acquire();
436 }
437 }
438 };
439
contendedQMutexLocker()440 void tst_QMutex::contendedQMutexLocker()
441 {
442 QFETCH(int, iterations);
443 QFETCH(int, msleepDuration);
444 QFETCH(bool, use2mutexes);
445
446 QMutex mutex1, mutex2;
447
448 QVector<QMutexLockerThread *> threads(threadCount);
449 for (int i = 0; i < threads.count(); ++i) {
450 threads[i] = new QMutexLockerThread(&mutex1, &mutex2, iterations, msleepDuration, use2mutexes);
451 threads[i]->start();
452 }
453
454 QBENCHMARK {
455 semaphore1.acquire(threadCount);
456 semaphore2.release(threadCount);
457 semaphore3.acquire(threadCount);
458 semaphore4.release(threadCount);
459 }
460
461 for (int i = 0; i < threads.count(); ++i)
462 threads[i]->done = true;
463 semaphore1.acquire(threadCount);
464 semaphore2.release(threadCount);
465 for (int i = 0; i < threads.count(); ++i)
466 threads[i]->wait();
467 qDeleteAll(threads);
468 }
469
470 QTEST_MAIN(tst_QMutex)
471 #include "tst_qmutex.moc"
472