1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <qcoreapplication.h>
33#include <qmutex.h>
34#include <qthread.h>
35#include <qwaitcondition.h>
36#include "qthreadonce.h"
37
38class tst_QThreadOnce : public QObject
39{
40 Q_OBJECT
41
42private slots:
43 void sameThread();
44 void sameThread_data();
45 void multipleThreads();
46
47 void nesting();
48 void reentering();
49#ifndef QT_NO_EXCEPTIONS
50 void exception();
51#endif
52};
53
54class SingletonObject: public QObject
55{
56 Q_OBJECT
57public:
58 static int runCount;
59 SingletonObject() { val.storeRelaxed(newValue: 42); ++runCount; }
60 ~SingletonObject() { }
61
62 QBasicAtomicInt val;
63};
64
65class IncrementThread: public QThread
66{
67public:
68 static QBasicAtomicInt runCount;
69 static QSingleton<SingletonObject> singleton;
70 QSemaphore &sem1, &sem2;
71 int &var;
72
73 IncrementThread(QSemaphore *psem1, QSemaphore *psem2, int *pvar, QObject *parent)
74 : QThread(parent), sem1(*psem1), sem2(*psem2), var(*pvar)
75 { start(); }
76
77 ~IncrementThread() { wait(); }
78
79protected:
80 void run()
81 {
82 sem2.release();
83 sem1.acquire(); // synchronize
84
85 Q_ONCE {
86 ++var;
87 }
88 runCount.ref();
89 singleton->val.ref();
90 }
91};
92int SingletonObject::runCount = 0;
93QBasicAtomicInt IncrementThread::runCount = Q_BASIC_ATOMIC_INITIALIZER(0);
94QSingleton<SingletonObject> IncrementThread::singleton;
95
96void tst_QThreadOnce::sameThread_data()
97{
98 SingletonObject::runCount = 0;
99 QTest::addColumn<int>(name: "expectedValue");
100
101 QTest::newRow(dataTag: "first") << 42;
102 QTest::newRow(dataTag: "second") << 43;
103}
104
105void tst_QThreadOnce::sameThread()
106{
107 static int controlVariable = 0;
108 Q_ONCE {
109 QCOMPARE(controlVariable, 0);
110 ++controlVariable;
111 }
112 QCOMPARE(controlVariable, 1);
113
114 static QSingleton<SingletonObject> s;
115 QTEST((int)s->val.loadRelaxed(), "expectedValue");
116 s->val.ref();
117
118 QCOMPARE(SingletonObject::runCount, 1);
119}
120
121void tst_QThreadOnce::multipleThreads()
122{
123#if defined(Q_OS_VXWORKS)
124 const int NumberOfThreads = 20;
125#else
126 const int NumberOfThreads = 100;
127#endif
128 int controlVariable = 0;
129 QSemaphore sem1, sem2(NumberOfThreads);
130
131 QObject *parent = new QObject;
132 for (int i = 0; i < NumberOfThreads; ++i)
133 new IncrementThread(&sem1, &sem2, &controlVariable, parent);
134
135 QCOMPARE(controlVariable, 0); // nothing must have set them yet
136 SingletonObject::runCount = 0;
137 IncrementThread::runCount.storeRelaxed(newValue: 0);
138
139 // wait for all of them to be ready
140 sem2.acquire(n: NumberOfThreads);
141 // unleash the threads
142 sem1.release(n: NumberOfThreads);
143
144 // wait for all of them to terminate:
145 delete parent;
146
147 QCOMPARE(controlVariable, 1);
148 QCOMPARE((int)IncrementThread::runCount.loadRelaxed(), NumberOfThreads);
149 QCOMPARE(SingletonObject::runCount, 1);
150}
151
152void tst_QThreadOnce::nesting()
153{
154 int variable = 0;
155 Q_ONCE {
156 Q_ONCE {
157 ++variable;
158 }
159 }
160
161 QCOMPARE(variable, 1);
162}
163
164static void reentrant(int control, int &counter)
165{
166 Q_ONCE {
167 if (counter)
168 reentrant(control: --control, counter);
169 ++counter;
170 }
171 static QSingleton<SingletonObject> s;
172 s->val.ref();
173}
174
175void tst_QThreadOnce::reentering()
176{
177 const int WantedRecursions = 5;
178 int count = 0;
179 SingletonObject::runCount = 0;
180 reentrant(control: WantedRecursions, counter&: count);
181
182 // reentrancy is undefined behavior:
183 QVERIFY(count == 1 || count == WantedRecursions);
184 QCOMPARE(SingletonObject::runCount, 1);
185}
186
187#if !defined(QT_NO_EXCEPTIONS)
188static void exception_helper(int &val)
189{
190 Q_ONCE {
191 if (val++ == 0) throw 0;
192 }
193}
194#endif
195
196#ifndef QT_NO_EXCEPTIONS
197void tst_QThreadOnce::exception()
198{
199 int count = 0;
200
201 try {
202 exception_helper(val&: count);
203 } catch (...) {
204 // nothing
205 }
206 QCOMPARE(count, 1);
207
208 try {
209 exception_helper(val&: count);
210 } catch (...) {
211 QVERIFY2(false, "Exception shouldn't have been thrown...");
212 }
213 QCOMPARE(count, 2);
214}
215#endif
216
217QTEST_MAIN(tst_QThreadOnce)
218#include "tst_qthreadonce.moc"
219

source code of qtbase/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp