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#include <QThread>
29
30struct TestIterator
31{
32 TestIterator(int i)
33 :i(i) { }
34
35 int operator-(const TestIterator &other)
36 {
37 return i - other.i;
38 }
39
40 TestIterator& operator++()
41 {
42 ++i;
43 return *this;
44 }
45
46 bool operator!=(const TestIterator &other) const
47 {
48 return i != other.i;
49 }
50
51 int i;
52};
53
54#include <qiterator.h>
55namespace std {
56template <>
57struct iterator_traits<TestIterator>
58{
59 typedef random_access_iterator_tag iterator_category;
60};
61
62int distance(TestIterator &a, TestIterator &b)
63{
64 return b - a;
65}
66
67}
68
69#include <qtconcurrentiteratekernel.h>
70#include <QtTest/QtTest>
71
72using namespace QtConcurrent;
73
74class tst_QtConcurrentIterateKernel: public QObject
75{
76 Q_OBJECT
77private slots:
78 // "for" iteration tests:
79 void instantiate();
80 void cancel();
81 void stresstest();
82 void noIterations();
83 void throttling();
84 void multipleResults();
85};
86
87QAtomicInt iterations;
88class PrintFor : public IterateKernel<TestIterator, void>
89{
90public:
91 PrintFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(begin, end) { iterations.storeRelaxed(newValue: 0); }
92 bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *)
93 {
94 iterations.fetchAndAddRelaxed(valueToAdd: end - begin);
95#ifdef PRINT
96 qDebug() << QThread::currentThread() << "iteration" << begin << "to" << end << "(exclusive)";
97#endif
98 return false;
99 }
100 bool runIteration(TestIterator it, int index , void *result)
101 {
102 return runIterations(it, begin: index, end: index + 1, result);
103 }
104
105};
106
107class SleepPrintFor : public IterateKernel<TestIterator, void>
108{
109public:
110 SleepPrintFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(begin, end) { iterations.storeRelaxed(newValue: 0); }
111 inline bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *)
112 {
113 QTest::qSleep(ms: 200);
114 iterations.fetchAndAddRelaxed(valueToAdd: end - begin);
115#ifdef PRINT
116 qDebug() << QThread::currentThread() << "iteration" << begin << "to" << end << "(exclusive)";
117#endif
118 return false;
119 }
120 bool runIteration(TestIterator it, int index , void *result)
121 {
122 return runIterations(it, begin: index, end: index + 1, result);
123 }
124};
125
126
127void tst_QtConcurrentIterateKernel::instantiate()
128{
129 auto future = startThreadEngine(threadEngine: new PrintFor(0, 40)).startAsynchronously();
130 future.waitForFinished();
131 QCOMPARE(iterations.loadRelaxed(), 40);
132}
133
134void tst_QtConcurrentIterateKernel::cancel()
135{
136 {
137 QFuture<void> f = startThreadEngine(threadEngine: new SleepPrintFor(0, 40)).startAsynchronously();
138 f.cancel();
139 f.waitForFinished();
140 QVERIFY(f.isCanceled());
141 // the threads might run one iteration each before they are canceled.
142 QVERIFY2(iterations.loadRelaxed() <= QThread::idealThreadCount(),
143 (QByteArray::number(iterations.loadRelaxed()) + ' ' + QByteArray::number(QThread::idealThreadCount())));
144 }
145}
146
147QAtomicInt counter;
148class CountFor : public IterateKernel<TestIterator, void>
149{
150public:
151 CountFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(begin, end) { iterations.storeRelaxed(newValue: 0); }
152 inline bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *)
153 {
154 counter.fetchAndAddRelaxed(valueToAdd: end - begin);
155 return false;
156 }
157 bool runIteration(TestIterator it, int index , void *result)
158 {
159 return runIterations(it, begin: index, end: index + 1, result);
160 }
161};
162
163void tst_QtConcurrentIterateKernel::stresstest()
164{
165 const int iterations = 1000;
166 const int times = 50;
167 for (int i = 0; i < times; ++i) {
168 counter.storeRelaxed(newValue: 0);
169 // ThreadEngine will delete f when it finishes
170 auto f = new CountFor(0, iterations);
171 auto future = f->startAsynchronously();
172 future.waitForFinished();
173 QCOMPARE(counter.loadRelaxed(), iterations);
174 }
175}
176
177void tst_QtConcurrentIterateKernel::noIterations()
178{
179 const int times = 20000;
180 for (int i = 0; i < times; ++i) {
181 auto future = startThreadEngine(threadEngine: new IterateKernel<TestIterator, void>(0, 0))
182 .startAsynchronously();
183 future.waitForFinished();
184 }
185}
186
187QMutex threadsMutex;
188QSet<QThread *> threads;
189class ThrottleFor : public IterateKernel<TestIterator, void>
190{
191public:
192 // this class throttles between iterations 100 and 200,
193 // and then records how many threads that run between
194 // iterations 140 and 160.
195 ThrottleFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, void>(begin, end) { iterations.storeRelaxed(newValue: 0); throttling = false; }
196 inline bool runIterations(TestIterator/*beginIterator*/, int begin, int end, void *)
197 {
198 if (200 >= begin && 200 < end) {
199 throttling = false;
200 }
201
202 iterations.fetchAndAddRelaxed(valueToAdd: end - begin);
203
204 QThread *thread = QThread::currentThread();
205
206 if (begin > 140 && end < 160) {
207 QMutexLocker locker(&threadsMutex);
208 threads.insert(value: thread);
209 }
210
211 if (100 >= begin && 100 < end) {
212 throttling = true;
213 }
214
215 QTest::qWait(ms: 1);
216
217 return false;
218 }
219 bool runIteration(TestIterator it, int index , void *result)
220 {
221 return runIterations(it, begin: index, end: index + 1, result);
222 }
223
224 bool shouldThrottleThread()
225 {
226 const int load = iterations.loadRelaxed();
227 return (load > 100 && load < 200);
228 }
229 bool throttling;
230};
231
232void tst_QtConcurrentIterateKernel::throttling()
233{
234 const int totalIterations = 400;
235 iterations.storeRelaxed(newValue: 0);
236
237 threads.clear();
238
239 // ThreadEngine will delete f when it finishes
240 auto f = new ThrottleFor(0, totalIterations);
241 auto future = f->startAsynchronously();
242 future.waitForFinished();
243
244 QCOMPARE(iterations.loadRelaxed(), totalIterations);
245
246
247 QCOMPARE(threads.count(), 1);
248}
249
250class MultipleResultsFor : public IterateKernel<TestIterator, int>
251{
252public:
253 MultipleResultsFor(TestIterator begin, TestIterator end) : IterateKernel<TestIterator, int>(begin, end) { }
254 inline bool runIterations(TestIterator, int begin, int end, int *results)
255 {
256 for (int i = begin; i < end; ++i)
257 results[i - begin] = i;
258 return true;
259 }
260};
261
262void tst_QtConcurrentIterateKernel::multipleResults()
263{
264 QFuture<int> f = startThreadEngine(threadEngine: new MultipleResultsFor(0, 10)).startAsynchronously();
265 QCOMPARE(f.results().count() , 10);
266 QCOMPARE(f.resultAt(0), 0);
267 QCOMPARE(f.resultAt(5), 5);
268 QCOMPARE(f.resultAt(9), 9);
269 f.waitForFinished();
270}
271
272QTEST_MAIN(tst_QtConcurrentIterateKernel)
273
274#include "tst_qtconcurrentiteratekernel.moc"
275

source code of qtbase/tests/auto/concurrent/qtconcurrentiteratekernel/tst_qtconcurrentiteratekernel.cpp