1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
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 <QtTest/QtTest>
31
32#include <QAtomicInt>
33#include <QCoreApplication>
34#include <QElapsedTimer>
35
36#include <limits.h>
37
38class tst_QAtomicInt : public QObject
39{
40 Q_OBJECT
41
42private slots:
43 void warningFree();
44 void alignment();
45
46 // QAtomicInt members
47 void constructor_data();
48 void constructor();
49 void copy_constructor_data();
50 void copy_constructor();
51 void assignment_operator_data();
52 void assignment_operator();
53
54 void isReferenceCountingNative();
55 void isReferenceCountingWaitFree();
56 void ref_data();
57 void ref();
58 void deref_data();
59 void deref();
60
61 void isTestAndSetNative();
62 void isTestAndSetWaitFree();
63 void testAndSet_data();
64 void testAndSet();
65
66 void isFetchAndStoreNative();
67 void isFetchAndStoreWaitFree();
68 void fetchAndStore_data();
69 void fetchAndStore();
70
71 void isFetchAndAddNative();
72 void isFetchAndAddWaitFree();
73 void fetchAndAdd_data();
74 void fetchAndAdd();
75
76 void operators();
77
78 // stress tests
79 void testAndSet_loop();
80 void fetchAndAdd_loop();
81 void fetchAndAdd_threadedLoop();
82
83private:
84 static void warningFreeHelper();
85};
86
87template <int I>
88static inline void assemblyMarker(void *ptr = 0)
89{
90 puts(s: (char *)ptr + I);
91}
92
93template <typename T, typename Atomic>
94static void warningFreeHelperTemplate()
95{
96 T expectedValue = 0;
97 T newValue = 0;
98 T valueToAdd = 0;
99
100 // the marker calls are here only to provide a divider for
101 // those reading the assembly output
102 assemblyMarker<0>();
103 Atomic i = Q_BASIC_ATOMIC_INITIALIZER(0);
104 printf(format: "%d\n", int(i.loadAcquire()));
105 assemblyMarker<1>(&i);
106
107 // the loads sometimes generate no assembly output
108 i.loadRelaxed();
109 assemblyMarker<11>(&i);
110 i.loadAcquire();
111 assemblyMarker<12>(&i);
112
113 i.storeRelaxed(newValue);
114 assemblyMarker<21>(&i);
115 i.storeRelease(newValue);
116 assemblyMarker<22>(&i);
117
118 i.ref();
119 assemblyMarker<31>(&i);
120 i.deref();
121 assemblyMarker<32>(&i);
122
123 i.testAndSetRelaxed(expectedValue, newValue);
124 assemblyMarker<41>(&i);
125 i.testAndSetAcquire(expectedValue, newValue);
126 assemblyMarker<42>(&i);
127 i.testAndSetRelease(expectedValue, newValue);
128 assemblyMarker<43>(&i);
129 i.testAndSetOrdered(expectedValue, newValue);
130 assemblyMarker<44>(&i);
131
132 i.fetchAndStoreRelaxed(newValue);
133 assemblyMarker<51>(&i);
134 i.fetchAndStoreAcquire(newValue);
135 assemblyMarker<52>(&i);
136 i.fetchAndStoreRelease(newValue);
137 assemblyMarker<53>(&i);
138 i.fetchAndStoreOrdered(newValue);
139 assemblyMarker<54>(&i);
140
141 i.fetchAndAddRelaxed(valueToAdd);
142 assemblyMarker<61>(&i);
143 i.fetchAndAddAcquire(valueToAdd);
144 assemblyMarker<62>(&i);
145 i.fetchAndAddRelease(valueToAdd);
146 assemblyMarker<63>(&i);
147 i.fetchAndAddOrdered(valueToAdd);
148 assemblyMarker<64>(&i);
149}
150
151template <bool> inline void booleanHelper()
152{ }
153
154template <typename Atomic>
155static void constexprFunctionsHelperTemplate()
156{
157#ifdef Q_COMPILER_CONSTEXPR
158 // this is a compile-time test only
159 booleanHelper<Atomic::isReferenceCountingNative()>();
160 booleanHelper<Atomic::isReferenceCountingWaitFree()>();
161 booleanHelper<Atomic::isTestAndSetNative()>();
162 booleanHelper<Atomic::isTestAndSetWaitFree()>();
163 booleanHelper<Atomic::isFetchAndStoreNative()>();
164 booleanHelper<Atomic::isFetchAndStoreWaitFree()>();
165 booleanHelper<Atomic::isFetchAndAddNative()>();
166 booleanHelper<Atomic::isFetchAndAddWaitFree()>();
167#endif
168}
169
170void tst_QAtomicInt::warningFreeHelper()
171{
172 qFatal(msg: "This code is bogus, and shouldn't be run. We're looking for compiler warnings only.");
173 warningFreeHelperTemplate<int, QBasicAtomicInt>();
174
175 // 32-bit are always supported:
176 warningFreeHelperTemplate<int, QBasicAtomicInteger<int> >();
177 warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<unsigned int> >();
178 constexprFunctionsHelperTemplate<QBasicAtomicInteger<int> >();
179 constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned int> >();
180# ifdef Q_COMPILER_UNICODE_STRINGS
181 warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char32_t> >();
182 constexprFunctionsHelperTemplate<QBasicAtomicInteger<char32_t> >();
183# endif
184
185 // pointer-sized integers are always supported:
186 warningFreeHelperTemplate<int, QBasicAtomicInteger<qptrdiff> >();
187 warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<quintptr> >();
188 constexprFunctionsHelperTemplate<QBasicAtomicInteger<qptrdiff> >();
189 constexprFunctionsHelperTemplate<QBasicAtomicInteger<quintptr> >();
190
191 // long is always supported because it's either 32-bit or pointer-sized:
192 warningFreeHelperTemplate<int, QBasicAtomicInteger<long int> >();
193 warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<unsigned long int> >();
194 constexprFunctionsHelperTemplate<QBasicAtomicInteger<long int> >();
195 constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned long int> >();
196
197#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
198 warningFreeHelperTemplate<qint16, QBasicAtomicInteger<qint16> >();
199 warningFreeHelperTemplate<quint16, QBasicAtomicInteger<quint16> >();
200 constexprFunctionsHelperTemplate<QBasicAtomicInteger<qint16> >();
201 constexprFunctionsHelperTemplate<QBasicAtomicInteger<quint16> >();
202# ifdef Q_COMPILER_UNICODE_STRINGS
203 warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char16_t> >();
204 constexprFunctionsHelperTemplate<QBasicAtomicInteger<char16_t> >();
205# endif
206#endif
207
208#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
209 warningFreeHelperTemplate<char, QBasicAtomicInteger<char> >();
210 warningFreeHelperTemplate<signed char, QBasicAtomicInteger<signed char> >();
211 warningFreeHelperTemplate<unsigned char, QBasicAtomicInteger<unsigned char> >();
212 constexprFunctionsHelperTemplate<QBasicAtomicInteger<char> >();
213 constexprFunctionsHelperTemplate<QBasicAtomicInteger<signed char> >();
214 constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned char> >();
215#endif
216
217#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
218#if !defined(__i386__) || (defined(Q_CC_GNU) && defined(__OPTIMIZE__))
219 warningFreeHelperTemplate<qlonglong, QBasicAtomicInteger<qlonglong> >();
220 warningFreeHelperTemplate<qulonglong, QBasicAtomicInteger<qulonglong> >();
221 constexprFunctionsHelperTemplate<QBasicAtomicInteger<qlonglong> >();
222 constexprFunctionsHelperTemplate<QBasicAtomicInteger<qulonglong> >();
223#endif
224#endif
225}
226
227void tst_QAtomicInt::warningFree()
228{
229 // This is a compile time check for warnings.
230 // No need for actual work here.
231
232 void (*foo)() = &warningFreeHelper;
233 (void)foo;
234}
235
236template <typename T> struct TypeInStruct { T type; };
237
238void tst_QAtomicInt::alignment()
239{
240#ifdef Q_ALIGNOF
241 // this will cause a build error if the alignment isn't the same
242 char dummy1[Q_ALIGNOF(QBasicAtomicInt) == Q_ALIGNOF(TypeInStruct<int>) ? 1 : -1];
243 char dummy2[Q_ALIGNOF(QAtomicInt) == Q_ALIGNOF(TypeInStruct<int>) ? 1 : -1];
244 (void)dummy1; (void)dummy2;
245
246#ifdef Q_ATOMIC_INT32_IS_SUPPORTED
247 QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<int>), Q_ALIGNOF(TypeInStruct<int>));
248#endif
249
250#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
251 QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<short>), Q_ALIGNOF(TypeInStruct<short>));
252#endif
253
254#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
255 QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<char>), Q_ALIGNOF(TypeInStruct<char>));
256#endif
257
258#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
259 QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<qlonglong>), Q_ALIGNOF(TypeInStruct<qlonglong>));
260#endif
261
262#endif
263}
264
265void tst_QAtomicInt::constructor_data()
266{
267 QTest::addColumn<int>(name: "value");
268
269 QTest::newRow(dataTag: "0") << 31337;
270 QTest::newRow(dataTag: "1") << 0;
271 QTest::newRow(dataTag: "2") << 1;
272 QTest::newRow(dataTag: "3") << -1;
273 QTest::newRow(dataTag: "4") << 2;
274 QTest::newRow(dataTag: "5") << -2;
275 QTest::newRow(dataTag: "6") << 3;
276 QTest::newRow(dataTag: "7") << -3;
277 QTest::newRow(dataTag: "8") << INT_MAX;
278 QTest::newRow(dataTag: "9") << INT_MIN+1;
279}
280
281void tst_QAtomicInt::constructor()
282{
283 QFETCH(int, value);
284 QAtomicInt atomic1(value);
285 QCOMPARE(atomic1.loadRelaxed(), value);
286 QAtomicInt atomic2 = value;
287 QCOMPARE(atomic2.loadRelaxed(), value);
288}
289
290void tst_QAtomicInt::copy_constructor_data()
291{ constructor_data(); }
292
293void tst_QAtomicInt::copy_constructor()
294{
295 QFETCH(int, value);
296 QAtomicInt atomic1(value);
297 QCOMPARE(atomic1.loadRelaxed(), value);
298
299 QAtomicInt atomic2(atomic1);
300 QCOMPARE(atomic2.loadRelaxed(), value);
301 QAtomicInt atomic3 = atomic1;
302 QCOMPARE(atomic3.loadRelaxed(), value);
303 QAtomicInt atomic4(atomic2);
304 QCOMPARE(atomic4.loadRelaxed(), value);
305 QAtomicInt atomic5 = atomic2;
306 QCOMPARE(atomic5.loadRelaxed(), value);
307}
308
309void tst_QAtomicInt::assignment_operator_data()
310{
311 QTest::addColumn<int>(name: "value");
312 QTest::addColumn<int>(name: "newval");
313
314 QTest::newRow(dataTag: "value0") << 0 << 1;
315 QTest::newRow(dataTag: "value1") << 1 << 0;
316 QTest::newRow(dataTag: "value2") << 0 << -1;
317 QTest::newRow(dataTag: "value3") << -1 << 0;
318 QTest::newRow(dataTag: "value4") << -1 << 1;
319 QTest::newRow(dataTag: "value5") << 1 << -1;
320}
321
322void tst_QAtomicInt::assignment_operator()
323{
324 QFETCH(int, value);
325 QFETCH(int, newval);
326
327 {
328 QAtomicInt atomic1 = value;
329 atomic1 = newval;
330 QCOMPARE(atomic1.loadRelaxed(), newval);
331 atomic1 = value;
332 QCOMPARE(atomic1.loadRelaxed(), value);
333
334 QAtomicInt atomic2 = newval;
335 atomic1 = atomic2;
336 QCOMPARE(atomic1.loadRelaxed(), atomic2.loadRelaxed());
337 }
338}
339
340void tst_QAtomicInt::isReferenceCountingNative()
341{
342#if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE)
343 // the runtime test should say the same thing
344 QVERIFY(QAtomicInt::isReferenceCountingNative());
345
346# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE) \
347 || defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE))
348# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
349# endif
350#elif defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE)
351 // could be either, just want to make sure the function is implemented
352 QVERIFY(QAtomicInt::isReferenceCountingNative() || !QAtomicInt::isReferenceCountingNative());
353
354# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) \
355 || defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE))
356# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
357# endif
358#elif defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE)
359 // the runtime test should say the same thing
360 QVERIFY(!QAtomicInt::isReferenceCountingNative());
361
362# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) \
363 || defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE))
364# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
365# endif
366#else
367# error "Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
368#endif
369}
370
371void tst_QAtomicInt::isReferenceCountingWaitFree()
372{
373#if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE)
374 // the runtime test should say the same thing
375 QVERIFY(QAtomicInt::isReferenceCountingWaitFree());
376
377 // enforce some invariants
378 QVERIFY(QAtomicInt::isReferenceCountingNative());
379# if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE)
380# error "Reference counting cannot be wait-free and unsupported at the same time!"
381# endif
382#else
383 // the runtime test should say the same thing
384 QVERIFY(!QAtomicInt::isReferenceCountingWaitFree());
385#endif
386}
387
388void tst_QAtomicInt::ref_data()
389{
390 QTest::addColumn<int>(name: "value");
391 QTest::addColumn<int>(name: "result");
392 QTest::addColumn<int>(name: "expected");
393
394 QTest::newRow(dataTag: "data0") << 0 << 1 << 1;
395 QTest::newRow(dataTag: "data1") << -1 << 0 << 0;
396 QTest::newRow(dataTag: "data2") << 1 << 1 << 2;
397}
398
399void tst_QAtomicInt::ref()
400{
401 QFETCH(int, value);
402 QAtomicInt x = value;
403 QTEST(x.ref() ? 1 : 0, "result");
404 QTEST(x.loadRelaxed(), "expected");
405}
406
407void tst_QAtomicInt::deref_data()
408{
409 QTest::addColumn<int>(name: "value");
410 QTest::addColumn<int>(name: "result");
411 QTest::addColumn<int>(name: "expected");
412
413 QTest::newRow(dataTag: "data0") << 0 << 1 << -1;
414 QTest::newRow(dataTag: "data1") << 1 << 0 << 0;
415 QTest::newRow(dataTag: "data2") << 2 << 1 << 1;
416}
417
418void tst_QAtomicInt::deref()
419{
420 QFETCH(int, value);
421 QAtomicInt x = value;
422 QTEST(x.deref() ? 1 : 0, "result");
423 QTEST(x.loadRelaxed(), "expected");
424}
425
426void tst_QAtomicInt::isTestAndSetNative()
427{
428#if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE)
429 // the runtime test should say the same thing
430 QVERIFY(QAtomicInt::isTestAndSetNative());
431
432# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE) \
433 || defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE))
434# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
435# endif
436#elif defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE)
437 // could be either, just want to make sure the function is implemented
438 QVERIFY(QAtomicInt::isTestAndSetNative() || !QAtomicInt::isTestAndSetNative());
439
440# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) \
441 || defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE))
442# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
443# endif
444#elif defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE)
445 // the runtime test should say the same thing
446 QVERIFY(!QAtomicInt::isTestAndSetNative());
447
448# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) \
449 || defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE))
450# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
451# endif
452#else
453# error "Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
454#endif
455}
456
457void tst_QAtomicInt::isTestAndSetWaitFree()
458{
459#if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE)
460 // the runtime test should say the same thing
461 QVERIFY(QAtomicInt::isTestAndSetWaitFree());
462
463 // enforce some invariants
464 QVERIFY(QAtomicInt::isTestAndSetNative());
465# if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE)
466# error "Reference counting cannot be wait-free and unsupported at the same time!"
467# endif
468#else
469 // the runtime test should say the same thing
470 QVERIFY(!QAtomicInt::isTestAndSetWaitFree());
471#endif
472}
473
474void tst_QAtomicInt::testAndSet_data()
475{
476 QTest::addColumn<int>(name: "value");
477 QTest::addColumn<int>(name: "expected");
478 QTest::addColumn<int>(name: "newval");
479 QTest::addColumn<bool>(name: "result");
480
481 // these should succeed
482 QTest::newRow(dataTag: "success0") << 0 << 0 << 0 << true;
483 QTest::newRow(dataTag: "success1") << 0 << 0 << 1 << true;
484 QTest::newRow(dataTag: "success2") << 0 << 0 << -1 << true;
485 QTest::newRow(dataTag: "success3") << 1 << 1 << 0 << true;
486 QTest::newRow(dataTag: "success4") << 1 << 1 << 1 << true;
487 QTest::newRow(dataTag: "success5") << 1 << 1 << -1 << true;
488 QTest::newRow(dataTag: "success6") << -1 << -1 << 0 << true;
489 QTest::newRow(dataTag: "success7") << -1 << -1 << 1 << true;
490 QTest::newRow(dataTag: "success8") << -1 << -1 << -1 << true;
491 QTest::newRow(dataTag: "success9") << INT_MIN+1 << INT_MIN+1 << INT_MIN+1 << true;
492 QTest::newRow(dataTag: "successA") << INT_MIN+1 << INT_MIN+1 << 1 << true;
493 QTest::newRow(dataTag: "successB") << INT_MIN+1 << INT_MIN+1 << -1 << true;
494 QTest::newRow(dataTag: "successC") << INT_MAX << INT_MAX << INT_MAX << true;
495 QTest::newRow(dataTag: "successD") << INT_MAX << INT_MAX << 1 << true;
496 QTest::newRow(dataTag: "successE") << INT_MAX << INT_MAX << -1 << true;
497
498 // these should fail
499 QTest::newRow(dataTag: "failure0") << 0 << 1 << ~0 << false;
500 QTest::newRow(dataTag: "failure1") << 0 << -1 << ~0 << false;
501 QTest::newRow(dataTag: "failure2") << 1 << 0 << ~0 << false;
502 QTest::newRow(dataTag: "failure3") << -1 << 0 << ~0 << false;
503 QTest::newRow(dataTag: "failure4") << 1 << -1 << ~0 << false;
504 QTest::newRow(dataTag: "failure5") << -1 << 1 << ~0 << false;
505 QTest::newRow(dataTag: "failure6") << INT_MIN+1 << INT_MAX << ~0 << false;
506 QTest::newRow(dataTag: "failure7") << INT_MAX << INT_MIN+1 << ~0 << false;
507}
508
509void tst_QAtomicInt::testAndSet()
510{
511 QFETCH(int, value);
512 QFETCH(int, expected);
513 QFETCH(int, newval);
514
515 {
516 QAtomicInt atomic = value;
517 QTEST(atomic.testAndSetRelaxed(expected, newval), "result");
518 }
519
520 {
521 QAtomicInt atomic = value;
522 QTEST(atomic.testAndSetAcquire(expected, newval), "result");
523 }
524
525 {
526 QAtomicInt atomic = value;
527 QTEST(atomic.testAndSetRelease(expected, newval), "result");
528 }
529
530 {
531 QAtomicInt atomic = value;
532 QTEST(atomic.testAndSetOrdered(expected, newval), "result");
533 }
534
535#ifdef Q_ATOMIC_INT32_IS_SUPPORTED
536 QFETCH(bool, result);
537 // the new implementation has the version that loads the current value
538
539 {
540 QAtomicInt atomic = value;
541 int currentval = 0xdeadbeef;
542 QCOMPARE(atomic.testAndSetRelaxed(expected, newval, currentval), result);
543 if (!result)
544 QCOMPARE(currentval, value);
545 }
546
547 {
548 QAtomicInt atomic = value;
549 int currentval = 0xdeadbeef;
550 QCOMPARE(atomic.testAndSetAcquire(expected, newval, currentval), result);
551 if (!result)
552 QCOMPARE(currentval, value);
553 }
554
555 {
556 QAtomicInt atomic = value;
557 int currentval = 0xdeadbeef;
558 QCOMPARE(atomic.testAndSetRelease(expected, newval, currentval), result);
559 if (!result)
560 QCOMPARE(currentval, value);
561 }
562
563 {
564 QAtomicInt atomic = value;
565 int currentval = 0xdeadbeef;
566 QCOMPARE(atomic.testAndSetOrdered(expected, newval, currentval), result);
567 if (!result)
568 QCOMPARE(currentval, value);
569 }
570#endif
571}
572
573void tst_QAtomicInt::isFetchAndStoreNative()
574{
575#if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE)
576 // the runtime test should say the same thing
577 QVERIFY(QAtomicInt::isFetchAndStoreNative());
578
579# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) \
580 || defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE))
581# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
582# endif
583#elif defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE)
584 // could be either, just want to make sure the function is implemented
585 QVERIFY(QAtomicInt::isFetchAndStoreNative() || !QAtomicInt::isFetchAndStoreNative());
586
587# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \
588 || defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE))
589# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
590# endif
591#elif defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE)
592 // the runtime test should say the same thing
593 QVERIFY(!QAtomicInt::isFetchAndStoreNative());
594
595# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \
596 || defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE))
597# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
598# endif
599#else
600# error "Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
601#endif
602}
603
604void tst_QAtomicInt::isFetchAndStoreWaitFree()
605{
606#if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE)
607 // the runtime test should say the same thing
608 QVERIFY(QAtomicInt::isFetchAndStoreWaitFree());
609
610 // enforce some invariants
611 QVERIFY(QAtomicInt::isFetchAndStoreNative());
612# if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE)
613# error "Reference counting cannot be wait-free and unsupported at the same time!"
614# endif
615#else
616 // the runtime test should say the same thing
617 QVERIFY(!QAtomicInt::isFetchAndStoreWaitFree());
618#endif
619}
620
621void tst_QAtomicInt::fetchAndStore_data()
622{
623 QTest::addColumn<int>(name: "value");
624 QTest::addColumn<int>(name: "newval");
625
626 QTest::newRow(dataTag: "data0") << 0 << 1;
627 QTest::newRow(dataTag: "data1") << 1 << 2;
628 QTest::newRow(dataTag: "data2") << 3 << 8;
629}
630
631void tst_QAtomicInt::fetchAndStore()
632{
633 QFETCH(int, value);
634 QFETCH(int, newval);
635
636 {
637 QAtomicInt atomic = value;
638 QCOMPARE(atomic.fetchAndStoreRelaxed(newval), value);
639 QCOMPARE(atomic.loadRelaxed(), newval);
640 }
641
642 {
643 QAtomicInt atomic = value;
644 QCOMPARE(atomic.fetchAndStoreAcquire(newval), value);
645 QCOMPARE(atomic.loadRelaxed(), newval);
646 }
647
648 {
649 QAtomicInt atomic = value;
650 QCOMPARE(atomic.fetchAndStoreRelease(newval), value);
651 QCOMPARE(atomic.loadRelaxed(), newval);
652 }
653
654 {
655 QAtomicInt atomic = value;
656 QCOMPARE(atomic.fetchAndStoreOrdered(newval), value);
657 QCOMPARE(atomic.loadRelaxed(), newval);
658 }
659}
660
661void tst_QAtomicInt::isFetchAndAddNative()
662{
663#if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE)
664 // the runtime test should say the same thing
665 QVERIFY(QAtomicInt::isFetchAndAddNative());
666
667# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) \
668 || defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE))
669# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
670# endif
671#elif defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE)
672 // could be either, just want to make sure the function is implemented
673 QVERIFY(QAtomicInt::isFetchAndAddNative() || !QAtomicInt::isFetchAndAddNative());
674
675# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \
676 || defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE))
677# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
678# endif
679#elif defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE)
680 // the runtime test should say the same thing
681 QVERIFY(!QAtomicInt::isFetchAndAddNative());
682
683# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \
684 || defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE))
685# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
686# endif
687#else
688# error "Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
689#endif
690}
691
692void tst_QAtomicInt::isFetchAndAddWaitFree()
693{
694#if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE)
695 // the runtime test should say the same thing
696 QVERIFY(QAtomicInt::isFetchAndAddWaitFree());
697
698 // enforce some invariants
699 QVERIFY(QAtomicInt::isFetchAndAddNative());
700# if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE)
701# error "Reference counting cannot be wait-free and unsupported at the same time!"
702# endif
703#else
704 // the runtime test should say the same thing
705 QVERIFY(!QAtomicInt::isFetchAndAddWaitFree());
706#endif
707}
708
709void tst_QAtomicInt::fetchAndAdd_data()
710{
711 QTest::addColumn<int>(name: "value1");
712 QTest::addColumn<int>(name: "value2");
713
714 QTest::newRow(dataTag: "0+1") << 0 << 1;
715 QTest::newRow(dataTag: "1+0") << 1 << 0;
716 QTest::newRow(dataTag: "1+2") << 1 << 2;
717 QTest::newRow(dataTag: "2+1") << 2 << 1;
718 QTest::newRow(dataTag: "10+21") << 10 << 21;
719 QTest::newRow(dataTag: "31+40") << 31 << 40;
720 QTest::newRow(dataTag: "51+62") << 51 << 62;
721 QTest::newRow(dataTag: "72+81") << 72 << 81;
722 QTest::newRow(dataTag: "810+721") << 810 << 721;
723 QTest::newRow(dataTag: "631+540") << 631 << 540;
724 QTest::newRow(dataTag: "451+362") << 451 << 362;
725 QTest::newRow(dataTag: "272+181") << 272 << 181;
726 QTest::newRow(dataTag: "1810+8721") << 1810 << 8721;
727 QTest::newRow(dataTag: "3631+6540") << 3631 << 6540;
728 QTest::newRow(dataTag: "5451+4362") << 5451 << 4362;
729 QTest::newRow(dataTag: "7272+2181") << 7272 << 2181;
730
731 QTest::newRow(dataTag: "0+-1") << 0 << -1;
732 QTest::newRow(dataTag: "1+0") << 1 << 0;
733 QTest::newRow(dataTag: "1+-2") << 1 << -2;
734 QTest::newRow(dataTag: "2+-1") << 2 << -1;
735 QTest::newRow(dataTag: "10+-21") << 10 << -21;
736 QTest::newRow(dataTag: "31+-40") << 31 << -40;
737 QTest::newRow(dataTag: "51+-62") << 51 << -62;
738 QTest::newRow(dataTag: "72+-81") << 72 << -81;
739 QTest::newRow(dataTag: "810+-721") << 810 << -721;
740 QTest::newRow(dataTag: "631+-540") << 631 << -540;
741 QTest::newRow(dataTag: "451+-362") << 451 << -362;
742 QTest::newRow(dataTag: "272+-181") << 272 << -181;
743 QTest::newRow(dataTag: "1810+-8721") << 1810 << -8721;
744 QTest::newRow(dataTag: "3631+-6540") << 3631 << -6540;
745 QTest::newRow(dataTag: "5451+-4362") << 5451 << -4362;
746 QTest::newRow(dataTag: "7272+-2181") << 7272 << -2181;
747
748 QTest::newRow(dataTag: "0+1") << 0 << 1;
749 QTest::newRow(dataTag: "-1+0") << -1 << 0;
750 QTest::newRow(dataTag: "-1+2") << -1 << 2;
751 QTest::newRow(dataTag: "-2+1") << -2 << 1;
752 QTest::newRow(dataTag: "-10+21") << -10 << 21;
753 QTest::newRow(dataTag: "-31+40") << -31 << 40;
754 QTest::newRow(dataTag: "-51+62") << -51 << 62;
755 QTest::newRow(dataTag: "-72+81") << -72 << 81;
756 QTest::newRow(dataTag: "-810+721") << -810 << 721;
757 QTest::newRow(dataTag: "-631+540") << -631 << 540;
758 QTest::newRow(dataTag: "-451+362") << -451 << 362;
759 QTest::newRow(dataTag: "-272+181") << -272 << 181;
760 QTest::newRow(dataTag: "-1810+8721") << -1810 << 8721;
761 QTest::newRow(dataTag: "-3631+6540") << -3631 << 6540;
762 QTest::newRow(dataTag: "-5451+4362") << -5451 << 4362;
763 QTest::newRow(dataTag: "-7272+2181") << -7272 << 2181;
764}
765
766void tst_QAtomicInt::fetchAndAdd()
767{
768 QFETCH(int, value1);
769 QFETCH(int, value2);
770 int result;
771
772 {
773 QAtomicInt atomic = value1;
774 result = atomic.fetchAndAddRelaxed(valueToAdd: value2);
775 QCOMPARE(result, value1);
776 QCOMPARE(atomic.loadRelaxed(), value1 + value2);
777 }
778
779 {
780 QAtomicInt atomic = value1;
781 result = atomic.fetchAndAddAcquire(valueToAdd: value2);
782 QCOMPARE(result, value1);
783 QCOMPARE(atomic.loadRelaxed(), value1 + value2);
784 }
785
786 {
787 QAtomicInt atomic = value1;
788 result = atomic.fetchAndAddRelease(valueToAdd: value2);
789 QCOMPARE(result, value1);
790 QCOMPARE(atomic.loadRelaxed(), value1 + value2);
791 }
792
793 {
794 QAtomicInt atomic = value1;
795 result = atomic.fetchAndAddOrdered(valueToAdd: value2);
796 QCOMPARE(result, value1);
797 QCOMPARE(atomic.loadRelaxed(), value1 + value2);
798 }
799}
800
801void tst_QAtomicInt::operators()
802{
803 {
804 // Test that QBasicAtomicInt also has operator= and cast operators
805 // We've been using them for QAtomicInt elsewhere
806 QBasicAtomicInt atomic = Q_BASIC_ATOMIC_INITIALIZER(0);
807 atomic = 1;
808 QCOMPARE(int(atomic), 1);
809 }
810
811 QAtomicInt atomic = 0;
812 int x = ++atomic;
813 QCOMPARE(int(atomic), x);
814 QCOMPARE(int(atomic), 1);
815
816 x = atomic++;
817 QCOMPARE(int(atomic), x + 1);
818 QCOMPARE(int(atomic), 2);
819
820 x = atomic--;
821 QCOMPARE(int(atomic), x - 1);
822 QCOMPARE(int(atomic), 1);
823
824 x = --atomic;
825 QCOMPARE(int(atomic), x);
826 QCOMPARE(int(atomic), 0);
827
828 x = (atomic += 1);
829 QCOMPARE(int(atomic), x);
830 QCOMPARE(int(atomic), 1);
831
832 x = (atomic -= 1);
833 QCOMPARE(int(atomic), x);
834 QCOMPARE(int(atomic), 0);
835
836 x = (atomic |= 0xf);
837 QCOMPARE(int(atomic), x);
838 QCOMPARE(int(atomic), 0xf);
839
840 x = (atomic &= 0x17);
841 QCOMPARE(int(atomic), x);
842 QCOMPARE(int(atomic), 7);
843
844 x = (atomic ^= 0x14);
845 QCOMPARE(int(atomic), x);
846 QCOMPARE(int(atomic), 0x13);
847
848 x = (atomic ^= atomic);
849 QCOMPARE(int(atomic), x);
850 QCOMPARE(int(atomic), 0);
851}
852
853void tst_QAtomicInt::testAndSet_loop()
854{
855 QElapsedTimer stopWatch;
856 stopWatch.start();
857
858 int iterations = 10000000;
859
860 QAtomicInt val=0;
861 for (int i = 0; i < iterations; ++i) {
862 int v = val.loadRelaxed();
863 QVERIFY(val.testAndSetRelaxed(v, v+1));
864 if ((i % 1000) == 999) {
865 if (stopWatch.elapsed() > 60 * 1000) {
866 // This test shouldn't run for more than two minutes.
867 qDebug(msg: "Interrupted test after %d iterations (%.2f iterations/sec)",
868 i, (i * 1000.0) / double(stopWatch.elapsed()));
869 break;
870 }
871 }
872 }
873}
874
875void tst_QAtomicInt::fetchAndAdd_loop()
876{
877 int iterations = 10000000;
878#if defined (Q_OS_HPUX)
879 iterations = 1000000;
880#endif
881
882 QAtomicInt val=0;
883 for (int i = 0; i < iterations; ++i) {
884 const int prev = val.fetchAndAddRelaxed(valueToAdd: 1);
885 QCOMPARE(prev, val.loadRelaxed() -1);
886 }
887}
888
889class FetchAndAddThread : public QThread
890{
891public:
892 void run()
893 {
894
895 for (int i = 0; i < iterations; ++i)
896 val->fetchAndAddAcquire(valueToAdd: 1);
897
898 for (int i = 0; i < iterations; ++i)
899 val->fetchAndAddAcquire(valueToAdd: -1);
900
901 }
902QAtomicInt *val;
903int iterations;
904};
905
906
907void tst_QAtomicInt::fetchAndAdd_threadedLoop()
908{
909 QAtomicInt val;
910 FetchAndAddThread t1;
911 t1.val = &val;
912 t1.iterations = 1000000;
913
914 FetchAndAddThread t2;
915 t2.val = &val;
916 t2.iterations = 2000000;
917
918 t1.start();
919 t2.start();
920 t1.wait();
921 t2.wait();
922
923 QCOMPARE(val.loadRelaxed(), 0);
924}
925
926QTEST_MAIN(tst_QAtomicInt)
927#include "tst_qatomicint.moc"
928

source code of qtbase/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp