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