1/****************************************************************************
2**
3** Copyright (C) 2016 Thiago Macieira <thiago@kde.org>
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtCore/QThread>
31#include <QtTest/QtTest>
32
33#if defined(Q_OS_UNIX)
34#include <sys/resource.h>
35#endif
36
37class tst_QGlobalStatic : public QObject
38{
39 Q_OBJECT
40
41public Q_SLOTS:
42 void initTestCase();
43
44private Q_SLOTS:
45 void beforeInitialization();
46 void api();
47 void constVolatile();
48 void exception();
49 void catchExceptionAndRetry();
50 void threadStressTest();
51 void afterDestruction();
52};
53
54void tst_QGlobalStatic::initTestCase()
55{
56#if defined(Q_OS_UNIX)
57 // The tests create a lot of threads, which require file descriptors. On systems like
58 // OS X low defaults such as 256 as the limit for the number of simultaneously
59 // open files is not sufficient.
60 struct rlimit numFiles;
61 if (getrlimit(RLIMIT_NOFILE, rlimits: &numFiles) == 0 && numFiles.rlim_cur < 1024) {
62 numFiles.rlim_cur = qMin(a: rlim_t(1024), b: numFiles.rlim_max);
63 setrlimit(RLIMIT_NOFILE, rlimits: &numFiles);
64 }
65#endif
66}
67
68Q_GLOBAL_STATIC_WITH_ARGS(const int, constInt, (42))
69Q_GLOBAL_STATIC_WITH_ARGS(volatile int, volatileInt, (-47))
70
71void otherFunction()
72{
73 // never called
74 constInt();
75 volatileInt();
76}
77
78// do not initialize the following Q_GLOBAL_STATIC
79Q_GLOBAL_STATIC(int, checkedBeforeInitialization)
80void tst_QGlobalStatic::beforeInitialization()
81{
82 QVERIFY(!checkedBeforeInitialization.exists());
83 QVERIFY(!checkedBeforeInitialization.isDestroyed());
84}
85
86struct Type {
87 int i;
88};
89
90Q_GLOBAL_STATIC(Type, checkedAfterInitialization)
91void tst_QGlobalStatic::api()
92{
93 // check the API
94 QVERIFY((Type *)checkedAfterInitialization);
95 QVERIFY(checkedAfterInitialization());
96 *checkedAfterInitialization = Type();
97 *checkedAfterInitialization() = Type();
98
99 checkedAfterInitialization()->i = 47;
100 checkedAfterInitialization->i = 42;
101 QCOMPARE(checkedAfterInitialization()->i, 42);
102 checkedAfterInitialization()->i = 47;
103 QCOMPARE(checkedAfterInitialization->i, 47);
104
105 QVERIFY(checkedAfterInitialization.exists());
106 QVERIFY(!checkedAfterInitialization.isDestroyed());
107}
108
109void tst_QGlobalStatic::constVolatile()
110{
111 QCOMPARE(*constInt(), 42);
112 QCOMPARE((int)*volatileInt(), -47);
113 QCOMPARE(*constInt(), 42);
114 QCOMPARE((int)*volatileInt(), -47);
115}
116
117struct ThrowingType
118{
119 static QBasicAtomicInt constructedCount;
120 static QBasicAtomicInt destructedCount;
121 ThrowingType()
122 {
123 throw 0;
124 }
125
126 ThrowingType(QBasicAtomicInt &throwControl)
127 {
128 constructedCount.ref();
129 if (throwControl.fetchAndAddRelaxed(valueToAdd: -1) != 0)
130 throw 0;
131 }
132 ~ThrowingType() { destructedCount.ref(); }
133};
134
135QBasicAtomicInt ThrowingType::constructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
136QBasicAtomicInt ThrowingType::destructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
137
138Q_GLOBAL_STATIC(ThrowingType, throwingGS)
139void tst_QGlobalStatic::exception()
140{
141 bool exceptionCaught = false;
142 try {
143 throwingGS();
144 } catch (int) {
145 exceptionCaught = true;
146 }
147 QVERIFY(exceptionCaught);
148 QCOMPARE(Q_QGS_throwingGS::guard.loadRelaxed(), 0);
149 QVERIFY(!throwingGS.exists());
150 QVERIFY(!throwingGS.isDestroyed());
151}
152
153QBasicAtomicInt exceptionControlVar = Q_BASIC_ATOMIC_INITIALIZER(1);
154Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, exceptionGS, (exceptionControlVar))
155void tst_QGlobalStatic::catchExceptionAndRetry()
156{
157 if (exceptionControlVar.loadRelaxed() != 1)
158 QSKIP("This test cannot be run more than once");
159 ThrowingType::constructedCount.storeRelaxed(newValue: 0);
160 ThrowingType::destructedCount.storeRelaxed(newValue: 0);
161
162 bool exceptionCaught = false;
163 try {
164 exceptionGS();
165 } catch (int) {
166 exceptionCaught = true;
167 }
168 QCOMPARE(ThrowingType::constructedCount.loadRelaxed(), 1);
169 QVERIFY(exceptionCaught);
170
171 exceptionGS();
172 QCOMPARE(ThrowingType::constructedCount.loadRelaxed(), 2);
173}
174
175QBasicAtomicInt threadStressTestControlVar = Q_BASIC_ATOMIC_INITIALIZER(5);
176Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, threadStressTestGS, (threadStressTestControlVar))
177
178
179void tst_QGlobalStatic::threadStressTest()
180{
181 class ThreadStressTestThread: public QThread
182 {
183 public:
184 QReadWriteLock *lock;
185 void run()
186 {
187 QReadLocker l(lock);
188 //usleep(qrand() * 200 / RAND_MAX);
189 // thundering herd
190 try {
191 threadStressTestGS();
192 } catch (int) {
193 }
194 }
195 };
196
197 ThrowingType::constructedCount.storeRelaxed(newValue: 0);
198 ThrowingType::destructedCount.storeRelaxed(newValue: 0);
199 int expectedConstructionCount = threadStressTestControlVar.loadRelaxed() + 1;
200 if (expectedConstructionCount <= 0)
201 QSKIP("This test cannot be run more than once");
202
203 const int numThreads = 200;
204 ThreadStressTestThread threads[numThreads];
205 QReadWriteLock lock;
206 lock.lockForWrite();
207 for (int i = 0; i < numThreads; ++i) {
208 threads[i].lock = &lock;
209 threads[i].start();
210 }
211
212 // wait for all threads
213 // release the herd
214 lock.unlock();
215
216 for (int i = 0; i < numThreads; ++i)
217 threads[i].wait();
218
219 QCOMPARE(ThrowingType::constructedCount.loadAcquire(), expectedConstructionCount);
220 QCOMPARE(ThrowingType::destructedCount.loadAcquire(), 0);
221}
222
223Q_GLOBAL_STATIC(int, checkedAfterDestruction)
224void tst_QGlobalStatic::afterDestruction()
225{
226 // this test will not produce results now
227 // it will simply run some code on destruction (after the global statics have been deleted)
228 // if that fails, this will cause a crash
229
230 // static destruction is LIFO: so we must add our exit-time code before the
231 // global static is used for the first time
232 static struct RunAtExit {
233 ~RunAtExit() {
234 int *ptr = checkedAfterDestruction();
235 if (ptr)
236 qFatal(msg: "Global static is not null as was expected");
237 }
238 } runAtExit;
239 (void) runAtExit;
240
241 *checkedAfterDestruction = 42;
242}
243
244QTEST_APPLESS_MAIN(tst_QGlobalStatic);
245
246#include "tst_qglobalstatic.moc"
247

source code of qtbase/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp