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 <qtconcurrentrun.h> |
29 | #include <qfuture.h> |
30 | #include <QString> |
31 | #include <QtTest/QtTest> |
32 | |
33 | #include <atomic> |
34 | |
35 | using namespace QtConcurrent; |
36 | |
37 | class tst_QtConcurrentRun: public QObject |
38 | { |
39 | Q_OBJECT |
40 | private slots: |
41 | void runLightFunction(); |
42 | void runHeavyFunction(); |
43 | void returnValue(); |
44 | void functionObject(); |
45 | void memberFunctions(); |
46 | void implicitConvertibleTypes(); |
47 | void runWaitLoop(); |
48 | void pollForIsFinished(); |
49 | void recursive(); |
50 | #ifndef QT_NO_EXCEPTIONS |
51 | void exceptions(); |
52 | #endif |
53 | void functor(); |
54 | void lambda(); |
55 | void nullThreadPool(); |
56 | void nullThreadPoolNoLeak(); |
57 | }; |
58 | |
59 | void light() |
60 | { |
61 | qDebug(msg: "in function" ); |
62 | qDebug(msg: "done function" ); |
63 | } |
64 | |
65 | void heavy() |
66 | { |
67 | qDebug(msg: "in function" ); |
68 | QString str; |
69 | for (int i = 0; i < 1000000; ++i) |
70 | str.append(s: "a" ); |
71 | qDebug(msg: "done function" ); |
72 | } |
73 | |
74 | |
75 | void tst_QtConcurrentRun::runLightFunction() |
76 | { |
77 | qDebug(msg: "starting function" ); |
78 | QFuture<void> future = run(functionPointer: light); |
79 | qDebug(msg: "waiting" ); |
80 | future.waitForFinished(); |
81 | qDebug(msg: "done" ); |
82 | } |
83 | |
84 | void tst_QtConcurrentRun::runHeavyFunction() |
85 | { |
86 | QThreadPool pool; |
87 | qDebug(msg: "starting function" ); |
88 | QFuture<void> future = run(pool: &pool, functionPointer: heavy); |
89 | qDebug(msg: "waiting" ); |
90 | future.waitForFinished(); |
91 | qDebug(msg: "done" ); |
92 | } |
93 | |
94 | int returnInt0() |
95 | { |
96 | return 10; |
97 | } |
98 | |
99 | int returnInt1(int i) |
100 | { |
101 | return i; |
102 | } |
103 | |
104 | class A |
105 | { |
106 | public: |
107 | int member0() { return 10; } |
108 | int member1(int in) { return in; } |
109 | |
110 | typedef int result_type; |
111 | int operator()() { return 10; } |
112 | int operator()(int in) { return in; } |
113 | }; |
114 | |
115 | class AConst |
116 | { |
117 | public: |
118 | int member0() const { return 10; } |
119 | int member1(int in) const { return in; } |
120 | |
121 | typedef int result_type; |
122 | int operator()() const { return 10; } |
123 | int operator()(int in) const { return in; } |
124 | }; |
125 | |
126 | class ANoExcept |
127 | { |
128 | public: |
129 | int member0() noexcept { return 10; } |
130 | int member1(int in) noexcept { return in; } |
131 | |
132 | typedef int result_type; |
133 | int operator()() noexcept { return 10; } |
134 | int operator()(int in) noexcept { return in; } |
135 | }; |
136 | |
137 | class AConstNoExcept |
138 | { |
139 | public: |
140 | int member0() const noexcept { return 10; } |
141 | int member1(int in) const noexcept { return in; } |
142 | |
143 | typedef int result_type; |
144 | int operator()() const noexcept { return 10; } |
145 | int operator()(int in) const noexcept { return in; } |
146 | }; |
147 | |
148 | void tst_QtConcurrentRun::returnValue() |
149 | { |
150 | QThreadPool pool; |
151 | QFuture<int> f; |
152 | |
153 | f = run(functionPointer: returnInt0); |
154 | QCOMPARE(f.result(), 10); |
155 | f = run(pool: &pool, functionPointer: returnInt0); |
156 | QCOMPARE(f.result(), 10); |
157 | |
158 | A a; |
159 | f = run(object: &a, fn: &A::member0); |
160 | QCOMPARE(f.result(), 10); |
161 | f = run(pool: &pool, object: &a, fn: &A::member0); |
162 | QCOMPARE(f.result(), 10); |
163 | |
164 | f = run(object: &a, fn: &A::member1, arg1: 20); |
165 | QCOMPARE(f.result(), 20); |
166 | f = run(pool: &pool, object: &a, fn: &A::member1, arg1: 20); |
167 | QCOMPARE(f.result(), 20); |
168 | |
169 | f = run(object: a, fn: &A::member0); |
170 | QCOMPARE(f.result(), 10); |
171 | f = run(pool: &pool, object: a, fn: &A::member0); |
172 | QCOMPARE(f.result(), 10); |
173 | |
174 | f = run(object: a, fn: &A::member1, arg1: 20); |
175 | QCOMPARE(f.result(), 20); |
176 | f = run(pool: &pool, object: a, fn: &A::member1, arg1: 20); |
177 | QCOMPARE(f.result(), 20); |
178 | |
179 | f = run(functionObject: a); |
180 | QCOMPARE(f.result(), 10); |
181 | f = run(pool: &pool, functionObject: a); |
182 | QCOMPARE(f.result(), 10); |
183 | |
184 | f = run(functionObject: &a); |
185 | QCOMPARE(f.result(), 10); |
186 | f = run(pool: &pool, functionObject: &a); |
187 | QCOMPARE(f.result(), 10); |
188 | |
189 | f = run(functionObject: a, arg1: 20); |
190 | QCOMPARE(f.result(), 20); |
191 | f = run(pool: &pool, functionObject: a, arg1: 20); |
192 | QCOMPARE(f.result(), 20); |
193 | |
194 | f = run(functionObject: &a, arg1: 20); |
195 | QCOMPARE(f.result(), 20); |
196 | f = run(pool: &pool, functionObject: &a, arg1: 20); |
197 | QCOMPARE(f.result(), 20); |
198 | |
199 | const AConst aConst = AConst(); |
200 | f = run(object: &aConst, fn: &AConst::member0); |
201 | QCOMPARE(f.result(), 10); |
202 | f = run(pool: &pool, object: &aConst, fn: &AConst::member0); |
203 | QCOMPARE(f.result(), 10); |
204 | |
205 | f = run(object: &aConst, fn: &AConst::member1, arg1: 20); |
206 | QCOMPARE(f.result(), 20); |
207 | f = run(pool: &pool, object: &aConst, fn: &AConst::member1, arg1: 20); |
208 | QCOMPARE(f.result(), 20); |
209 | |
210 | f = run(object: aConst, fn: &AConst::member0); |
211 | QCOMPARE(f.result(), 10); |
212 | f = run(pool: &pool, object: aConst, fn: &AConst::member0); |
213 | QCOMPARE(f.result(), 10); |
214 | |
215 | f = run(object: aConst, fn: &AConst::member1, arg1: 20); |
216 | QCOMPARE(f.result(), 20); |
217 | f = run(pool: &pool, object: aConst, fn: &AConst::member1, arg1: 20); |
218 | QCOMPARE(f.result(), 20); |
219 | |
220 | f = run(functionObject: aConst); |
221 | QCOMPARE(f.result(), 10); |
222 | f = run(pool: &pool, functionObject: aConst); |
223 | QCOMPARE(f.result(), 10); |
224 | |
225 | f = run(functionObject: &aConst); |
226 | QCOMPARE(f.result(), 10); |
227 | f = run(pool: &pool, functionObject: &aConst); |
228 | QCOMPARE(f.result(), 10); |
229 | |
230 | f = run(functionObject: aConst, arg1: 20); |
231 | QCOMPARE(f.result(), 20); |
232 | f = run(pool: &pool, functionObject: aConst, arg1: 20); |
233 | QCOMPARE(f.result(), 20); |
234 | |
235 | f = run(functionObject: &aConst, arg1: 20); |
236 | QCOMPARE(f.result(), 20); |
237 | f = run(pool: &pool, functionObject: &aConst, arg1: 20); |
238 | QCOMPARE(f.result(), 20); |
239 | |
240 | ANoExcept aNoExcept; |
241 | f = run(object: &aNoExcept, fn: &ANoExcept::member0); |
242 | QCOMPARE(f.result(), 10); |
243 | f = run(pool: &pool, object: &aNoExcept, fn: &ANoExcept::member0); |
244 | QCOMPARE(f.result(), 10); |
245 | |
246 | f = run(object: &aNoExcept, fn: &ANoExcept::member1, arg1: 20); |
247 | QCOMPARE(f.result(), 20); |
248 | f = run(pool: &pool, object: &aNoExcept, fn: &ANoExcept::member1, arg1: 20); |
249 | QCOMPARE(f.result(), 20); |
250 | |
251 | f = run(object: aNoExcept, fn: &ANoExcept::member0); |
252 | QCOMPARE(f.result(), 10); |
253 | f = run(pool: &pool, object: aNoExcept, fn: &ANoExcept::member0); |
254 | QCOMPARE(f.result(), 10); |
255 | |
256 | f = run(object: aNoExcept, fn: &ANoExcept::member1, arg1: 20); |
257 | QCOMPARE(f.result(), 20); |
258 | f = run(pool: &pool, object: aNoExcept, fn: &ANoExcept::member1, arg1: 20); |
259 | QCOMPARE(f.result(), 20); |
260 | |
261 | f = run(functionObject: aNoExcept); |
262 | QCOMPARE(f.result(), 10); |
263 | f = run(pool: &pool, functionObject: aNoExcept); |
264 | QCOMPARE(f.result(), 10); |
265 | |
266 | f = run(functionObject: &aNoExcept); |
267 | QCOMPARE(f.result(), 10); |
268 | f = run(pool: &pool, functionObject: &aNoExcept); |
269 | QCOMPARE(f.result(), 10); |
270 | |
271 | f = run(functionObject: aNoExcept, arg1: 20); |
272 | QCOMPARE(f.result(), 20); |
273 | f = run(pool: &pool, functionObject: aNoExcept, arg1: 20); |
274 | QCOMPARE(f.result(), 20); |
275 | |
276 | f = run(functionObject: &aNoExcept, arg1: 20); |
277 | QCOMPARE(f.result(), 20); |
278 | f = run(pool: &pool, functionObject: &aNoExcept, arg1: 20); |
279 | QCOMPARE(f.result(), 20); |
280 | |
281 | const AConstNoExcept aConstNoExcept = AConstNoExcept(); |
282 | f = run(object: &aConstNoExcept, fn: &AConstNoExcept::member0); |
283 | QCOMPARE(f.result(), 10); |
284 | f = run(pool: &pool, object: &aConstNoExcept, fn: &AConstNoExcept::member0); |
285 | QCOMPARE(f.result(), 10); |
286 | |
287 | f = run(object: &aConstNoExcept, fn: &AConstNoExcept::member1, arg1: 20); |
288 | QCOMPARE(f.result(), 20); |
289 | f = run(pool: &pool, object: &aConstNoExcept, fn: &AConstNoExcept::member1, arg1: 20); |
290 | QCOMPARE(f.result(), 20); |
291 | |
292 | f = run(object: aConstNoExcept, fn: &AConstNoExcept::member0); |
293 | QCOMPARE(f.result(), 10); |
294 | f = run(pool: &pool, object: aConstNoExcept, fn: &AConstNoExcept::member0); |
295 | QCOMPARE(f.result(), 10); |
296 | |
297 | f = run(object: aConstNoExcept, fn: &AConstNoExcept::member1, arg1: 20); |
298 | QCOMPARE(f.result(), 20); |
299 | f = run(pool: &pool, object: aConstNoExcept, fn: &AConstNoExcept::member1, arg1: 20); |
300 | QCOMPARE(f.result(), 20); |
301 | |
302 | f = run(functionObject: aConstNoExcept); |
303 | QCOMPARE(f.result(), 10); |
304 | f = run(pool: &pool, functionObject: aConstNoExcept); |
305 | QCOMPARE(f.result(), 10); |
306 | |
307 | f = run(functionObject: &aConstNoExcept); |
308 | QCOMPARE(f.result(), 10); |
309 | f = run(pool: &pool, functionObject: &aConstNoExcept); |
310 | QCOMPARE(f.result(), 10); |
311 | |
312 | f = run(functionObject: aConstNoExcept, arg1: 20); |
313 | QCOMPARE(f.result(), 20); |
314 | f = run(pool: &pool, functionObject: aConstNoExcept, arg1: 20); |
315 | QCOMPARE(f.result(), 20); |
316 | |
317 | f = run(functionObject: &aConstNoExcept, arg1: 20); |
318 | QCOMPARE(f.result(), 20); |
319 | f = run(pool: &pool, functionObject: &aConstNoExcept, arg1: 20); |
320 | QCOMPARE(f.result(), 20); |
321 | } |
322 | |
323 | struct TestClass |
324 | { |
325 | void foo() { } |
326 | typedef void result_type; |
327 | void operator()() { } |
328 | void operator()(int) { } |
329 | void fooInt(int){ }; |
330 | }; |
331 | |
332 | struct TestConstClass |
333 | { |
334 | void foo() const { } |
335 | typedef void result_type; |
336 | void operator()() const { } |
337 | void operator()(int) const { } |
338 | void fooInt(int) const { }; |
339 | }; |
340 | |
341 | void tst_QtConcurrentRun::functionObject() |
342 | { |
343 | QThreadPool pool; |
344 | QFuture<void> f; |
345 | TestClass c; |
346 | |
347 | f = run(functionObject: c); |
348 | f = run(functionObject: &c); |
349 | f = run(functionObject: c, arg1: 10); |
350 | f = run(functionObject: &c, arg1: 10); |
351 | |
352 | f = run(pool: &pool, functionObject: c); |
353 | f = run(pool: &pool, functionObject: &c); |
354 | f = run(pool: &pool, functionObject: c, arg1: 10); |
355 | f = run(pool: &pool, functionObject: &c, arg1: 10); |
356 | |
357 | const TestConstClass cc = TestConstClass(); |
358 | f = run(functionObject: cc); |
359 | f = run(functionObject: &cc); |
360 | f = run(functionObject: cc, arg1: 10); |
361 | f = run(functionObject: &cc, arg1: 10); |
362 | |
363 | f = run(pool: &pool, functionObject: cc); |
364 | f = run(pool: &pool, functionObject: &cc); |
365 | f = run(pool: &pool, functionObject: cc, arg1: 10); |
366 | f = run(pool: &pool, functionObject: &cc, arg1: 10); |
367 | } |
368 | |
369 | |
370 | void tst_QtConcurrentRun::memberFunctions() |
371 | { |
372 | QThreadPool pool; |
373 | |
374 | TestClass c; |
375 | |
376 | run(object: c, fn: &TestClass::foo).waitForFinished(); |
377 | run(object: &c, fn: &TestClass::foo).waitForFinished(); |
378 | run(object: c, fn: &TestClass::fooInt, arg1: 10).waitForFinished(); |
379 | run(object: &c, fn: &TestClass::fooInt, arg1: 10).waitForFinished(); |
380 | |
381 | run(pool: &pool, object: c, fn: &TestClass::foo).waitForFinished(); |
382 | run(pool: &pool, object: &c, fn: &TestClass::foo).waitForFinished(); |
383 | run(pool: &pool, object: c, fn: &TestClass::fooInt, arg1: 10).waitForFinished(); |
384 | run(pool: &pool, object: &c, fn: &TestClass::fooInt, arg1: 10).waitForFinished(); |
385 | |
386 | const TestConstClass cc = TestConstClass(); |
387 | run(object: cc, fn: &TestConstClass::foo).waitForFinished(); |
388 | run(object: &cc, fn: &TestConstClass::foo).waitForFinished(); |
389 | run(object: cc, fn: &TestConstClass::fooInt, arg1: 10).waitForFinished(); |
390 | run(object: &cc, fn: &TestConstClass::fooInt, arg1: 10).waitForFinished(); |
391 | |
392 | run(pool: &pool, object: cc, fn: &TestConstClass::foo).waitForFinished(); |
393 | run(pool: &pool, object: &cc, fn: &TestConstClass::foo).waitForFinished(); |
394 | run(pool: &pool, object: cc, fn: &TestConstClass::fooInt, arg1: 10).waitForFinished(); |
395 | run(pool: &pool, object: &cc, fn: &TestConstClass::fooInt, arg1: 10).waitForFinished(); |
396 | } |
397 | |
398 | |
399 | void doubleFunction(double) |
400 | { |
401 | |
402 | } |
403 | |
404 | void stringConstRefFunction(const QString &) |
405 | { |
406 | |
407 | } |
408 | |
409 | void stringRefFunction(QString &) |
410 | { |
411 | |
412 | } |
413 | |
414 | void stringFunction(QString) |
415 | { |
416 | |
417 | } |
418 | |
419 | void stringIntFunction(QString) |
420 | { |
421 | |
422 | } |
423 | |
424 | |
425 | void tst_QtConcurrentRun::implicitConvertibleTypes() |
426 | { |
427 | QThreadPool pool; |
428 | |
429 | double d; |
430 | run(functionPointer: doubleFunction, arg1: d).waitForFinished(); |
431 | run(pool: &pool, functionPointer: doubleFunction, arg1: d).waitForFinished(); |
432 | int i; |
433 | run(functionPointer: doubleFunction, arg1: d).waitForFinished(); |
434 | run(pool: &pool, functionPointer: doubleFunction, arg1: d).waitForFinished(); |
435 | run(functionPointer: doubleFunction, arg1: i).waitForFinished(); |
436 | run(pool: &pool, functionPointer: doubleFunction, arg1: i).waitForFinished(); |
437 | run(functionPointer: doubleFunction, arg1: 10).waitForFinished(); |
438 | run(pool: &pool, functionPointer: doubleFunction, arg1: 10).waitForFinished(); |
439 | run(functionPointer: stringFunction, arg1: QLatin1String("Foo" )).waitForFinished(); |
440 | run(pool: &pool, functionPointer: stringFunction, arg1: QLatin1String("Foo" )).waitForFinished(); |
441 | run(functionPointer: stringConstRefFunction, arg1: QLatin1String("Foo" )).waitForFinished(); |
442 | run(pool: &pool, functionPointer: stringConstRefFunction, arg1: QLatin1String("Foo" )).waitForFinished(); |
443 | QString string; |
444 | run(functionPointer: stringRefFunction, arg1: string).waitForFinished(); |
445 | run(pool: &pool, functionPointer: stringRefFunction, arg1: string).waitForFinished(); |
446 | } |
447 | |
448 | void fn() { } |
449 | |
450 | void tst_QtConcurrentRun::runWaitLoop() |
451 | { |
452 | for (int i = 0; i < 1000; ++i) |
453 | run(functionPointer: fn).waitForFinished(); |
454 | } |
455 | |
456 | static bool allFinished(const QList<QFuture<void> > &futures) |
457 | { |
458 | auto hasNotFinished = [](const QFuture<void> &future) { return !future.isFinished(); }; |
459 | return std::find_if(first: futures.cbegin(), last: futures.cend(), pred: hasNotFinished) |
460 | == futures.constEnd(); |
461 | } |
462 | |
463 | static void runFunction() |
464 | { |
465 | QEventLoop loop; |
466 | QTimer::singleShot(interval: 20, receiver: &loop, slot: &QEventLoop::quit); |
467 | loop.exec(); |
468 | } |
469 | |
470 | void tst_QtConcurrentRun::pollForIsFinished() |
471 | { |
472 | const int numThreads = std::max(a: 4, b: 2 * QThread::idealThreadCount()); |
473 | QThreadPool::globalInstance()->setMaxThreadCount(numThreads); |
474 | |
475 | QFutureSynchronizer<void> synchronizer; |
476 | for (int i = 0; i < numThreads; ++i) |
477 | synchronizer.addFuture(future: QtConcurrent::run(functionPointer: &runFunction)); |
478 | |
479 | // same as synchronizer.waitForFinished() but with a timeout |
480 | QTRY_VERIFY(allFinished(synchronizer.futures())); |
481 | } |
482 | |
483 | |
484 | QAtomicInt count; |
485 | |
486 | void recursiveRun(int level) |
487 | { |
488 | count.ref(); |
489 | if (--level > 0) { |
490 | QFuture<void> f1 = run(functionPointer: recursiveRun, arg1: level); |
491 | QFuture<void> f2 = run(functionPointer: recursiveRun, arg1: level); |
492 | f1.waitForFinished(); |
493 | f2.waitForFinished(); |
494 | } |
495 | } |
496 | |
497 | int recursiveResult(int level) |
498 | { |
499 | count.ref(); |
500 | if (--level > 0) { |
501 | QFuture<int> f1 = run(functionPointer: recursiveResult, arg1: level); |
502 | QFuture<int> f2 = run(functionPointer: recursiveResult, arg1: level); |
503 | return f1.result() + f2.result(); |
504 | } |
505 | return 1; |
506 | } |
507 | |
508 | void tst_QtConcurrentRun::recursive() |
509 | { |
510 | int levels = 15; |
511 | |
512 | for (int i = 0; i < QThread::idealThreadCount(); ++i) { |
513 | count.storeRelaxed(newValue: 0); |
514 | QThreadPool::globalInstance()->setMaxThreadCount(i); |
515 | recursiveRun(level: levels); |
516 | QCOMPARE(count.loadRelaxed(), (int)std::pow(2.0, levels) - 1); |
517 | } |
518 | |
519 | for (int i = 0; i < QThread::idealThreadCount(); ++i) { |
520 | count.storeRelaxed(newValue: 0); |
521 | QThreadPool::globalInstance()->setMaxThreadCount(i); |
522 | recursiveResult(level: levels); |
523 | QCOMPARE(count.loadRelaxed(), (int)std::pow(2.0, levels) - 1); |
524 | } |
525 | } |
526 | |
527 | int e; |
528 | void vfn0() |
529 | { |
530 | ++e; |
531 | } |
532 | |
533 | int fn0() |
534 | { |
535 | return 1; |
536 | } |
537 | |
538 | void vfn1(double) |
539 | { |
540 | ++e; |
541 | } |
542 | |
543 | int fn1(int) |
544 | { |
545 | return 1; |
546 | } |
547 | |
548 | void vfn2(double, int *) |
549 | { |
550 | ++e; |
551 | } |
552 | |
553 | int fn2(double, int *) |
554 | { |
555 | return 1; |
556 | } |
557 | |
558 | |
559 | #ifndef QT_NO_EXCEPTIONS |
560 | void throwFunction() |
561 | { |
562 | throw QException(); |
563 | } |
564 | |
565 | int throwFunctionReturn() |
566 | { |
567 | throw QException(); |
568 | return 0; |
569 | } |
570 | |
571 | class SlowTask : public QRunnable |
572 | { |
573 | public: |
574 | static QAtomicInt cancel; |
575 | void run() override { |
576 | int iter = 60; |
577 | while (--iter && !cancel.loadRelaxed()) |
578 | QThread::currentThread()->msleep(25); |
579 | } |
580 | }; |
581 | |
582 | QAtomicInt SlowTask::cancel; |
583 | |
584 | void tst_QtConcurrentRun::exceptions() |
585 | { |
586 | QThreadPool pool; |
587 | bool caught; |
588 | |
589 | caught = false; |
590 | try { |
591 | QtConcurrent::run(functionPointer: throwFunction).waitForFinished(); |
592 | } catch (QException &) { |
593 | caught = true; |
594 | } |
595 | if (!caught) |
596 | QFAIL("did not get exception" ); |
597 | |
598 | caught = false; |
599 | try { |
600 | QtConcurrent::run(pool: &pool, functionPointer: throwFunction).waitForFinished(); |
601 | } catch (QException &) { |
602 | caught = true; |
603 | } |
604 | if (!caught) |
605 | QFAIL("did not get exception" ); |
606 | |
607 | caught = false; |
608 | try { |
609 | QtConcurrent::run(functionPointer: throwFunctionReturn).waitForFinished(); |
610 | } catch (QException &) { |
611 | caught = true; |
612 | } |
613 | if (!caught) |
614 | QFAIL("did not get exception" ); |
615 | |
616 | caught = false; |
617 | try { |
618 | QtConcurrent::run(pool: &pool, functionPointer: throwFunctionReturn).waitForFinished(); |
619 | } catch (QException &) { |
620 | caught = true; |
621 | } |
622 | if (!caught) |
623 | QFAIL("did not get exception" ); |
624 | |
625 | caught = false; |
626 | try { |
627 | QtConcurrent::run(pool: &pool, functionPointer: throwFunctionReturn).result(); |
628 | } catch (QException &) { |
629 | caught = true; |
630 | } |
631 | QVERIFY2(caught, "did not get exception" ); |
632 | |
633 | // Force the task to be run on this thread. |
634 | caught = false; |
635 | QThreadPool shortPool; |
636 | shortPool.setMaxThreadCount(1); |
637 | SlowTask *st = new SlowTask(); |
638 | try { |
639 | shortPool.start(runnable: st); |
640 | QtConcurrent::run(pool: &shortPool, functionPointer: throwFunctionReturn).result(); |
641 | } catch (QException &) { |
642 | caught = true; |
643 | } |
644 | |
645 | SlowTask::cancel.storeRelaxed(newValue: true); |
646 | |
647 | QVERIFY2(caught, "did not get exception" ); |
648 | } |
649 | #endif |
650 | |
651 | // Compiler supports decltype |
652 | struct Functor { |
653 | int operator()() { return 42; } |
654 | double operator()(double a, double b) { return a/b; } |
655 | int operator()(int a, int b) { return a/b; } |
656 | void operator()(int) { } |
657 | void operator()(int, int, int) { } |
658 | void operator()(int, int, int, int) { } |
659 | void operator()(int, int, int, int, int) { } |
660 | void operator()(int, int, int, int, int, int) { } |
661 | }; |
662 | |
663 | // This tests functor without result_type; decltype need to be supported by the compiler. |
664 | void tst_QtConcurrentRun::functor() |
665 | { |
666 | Functor f; |
667 | { |
668 | QFuture<int> fut = QtConcurrent::run(functor: f); |
669 | QCOMPARE(fut.result(), 42); |
670 | } |
671 | { |
672 | QFuture<double> fut = QtConcurrent::run(functor: f, arg1: 8.5, arg2: 1.8); |
673 | QCOMPARE(fut.result(), (8.5/1.8)); |
674 | } |
675 | { |
676 | QFuture<int> fut = QtConcurrent::run(functor: f, arg1: 19, arg2: 3); |
677 | QCOMPARE(fut.result(), int(19/3)); |
678 | } |
679 | { |
680 | QtConcurrent::run(functor: f, arg1: 1).waitForFinished(); |
681 | QtConcurrent::run(functor: f, arg1: 1,arg2: 2).waitForFinished(); |
682 | QtConcurrent::run(functor: f, arg1: 1,arg2: 2,arg3: 3).waitForFinished(); |
683 | QtConcurrent::run(functor: f, arg1: 1,arg2: 2,arg3: 3,arg4: 4).waitForFinished(); |
684 | QtConcurrent::run(functor: f, arg1: 1,arg2: 2,arg3: 3,arg4: 4,arg5: 5).waitForFinished(); |
685 | } |
686 | // and now with explicit pool: |
687 | QThreadPool pool; |
688 | { |
689 | QFuture<int> fut = QtConcurrent::run(pool: &pool, functor: f); |
690 | QCOMPARE(fut.result(), 42); |
691 | } |
692 | { |
693 | QFuture<double> fut = QtConcurrent::run(pool: &pool, functor: f, arg1: 8.5, arg2: 1.8); |
694 | QCOMPARE(fut.result(), (8.5/1.8)); |
695 | } |
696 | { |
697 | QFuture<int> fut = QtConcurrent::run(pool: &pool, functor: f, arg1: 19, arg2: 3); |
698 | QCOMPARE(fut.result(), int(19/3)); |
699 | } |
700 | { |
701 | QtConcurrent::run(pool: &pool, functor: f, arg1: 1).waitForFinished(); |
702 | QtConcurrent::run(pool: &pool, functor: f, arg1: 1,arg2: 2).waitForFinished(); |
703 | QtConcurrent::run(pool: &pool, functor: f, arg1: 1,arg2: 2,arg3: 3).waitForFinished(); |
704 | QtConcurrent::run(pool: &pool, functor: f, arg1: 1,arg2: 2,arg3: 3,arg4: 4).waitForFinished(); |
705 | QtConcurrent::run(pool: &pool, functor: f, arg1: 1,arg2: 2,arg3: 3,arg4: 4,arg5: 5).waitForFinished(); |
706 | } |
707 | } |
708 | |
709 | // Compiler supports lambda |
710 | void tst_QtConcurrentRun::lambda() |
711 | { |
712 | QCOMPARE(QtConcurrent::run([](){ return 45; }).result(), 45); |
713 | QCOMPARE(QtConcurrent::run([](int a){ return a+15; }, 12).result(), 12+15); |
714 | QCOMPARE(QtConcurrent::run([](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); |
715 | QCOMPARE(QtConcurrent::run([](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); |
716 | |
717 | { |
718 | QString str { "Hello World Foo" }; |
719 | QFuture<QStringList> f1 = QtConcurrent::run(functor: [&](){ return str.split(sep: ' '); }); |
720 | auto r = f1.result(); |
721 | QCOMPARE(r, QStringList({"Hello" , "World" , "Foo" })); |
722 | } |
723 | |
724 | // and now with explicit pool: |
725 | QThreadPool pool; |
726 | QCOMPARE(QtConcurrent::run(&pool, [](){ return 45; }).result(), 45); |
727 | QCOMPARE(QtConcurrent::run(&pool, [](int a){ return a+15; }, 12).result(), 12+15); |
728 | QCOMPARE(QtConcurrent::run(&pool, [](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); |
729 | QCOMPARE(QtConcurrent::run(&pool, [](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); |
730 | |
731 | { |
732 | QString str { "Hello World Foo" }; |
733 | QFuture<QStringList> f1 = QtConcurrent::run(pool: &pool, functor: [&](){ return str.split(sep: ' '); }); |
734 | auto r = f1.result(); |
735 | QCOMPARE(r, QStringList({"Hello" , "World" , "Foo" })); |
736 | } |
737 | } |
738 | |
739 | // QTBUG-98901 |
740 | void tst_QtConcurrentRun::nullThreadPool() |
741 | { |
742 | QThreadPool *pool = nullptr; |
743 | std::atomic<bool> isInvoked(false); |
744 | auto future = run(pool, functor: [&] { isInvoked = true; }); |
745 | future.waitForFinished(); |
746 | QVERIFY(future.isCanceled()); |
747 | QVERIFY(!isInvoked); |
748 | } |
749 | |
750 | struct LifetimeChecker |
751 | { |
752 | LifetimeChecker() { ++count; } |
753 | LifetimeChecker(const LifetimeChecker &) { ++count; } |
754 | ~LifetimeChecker() { --count; } |
755 | |
756 | void operator()() { } |
757 | |
758 | static std::atomic<int> count; |
759 | }; |
760 | std::atomic<int> LifetimeChecker::count{ 0 }; |
761 | |
762 | void tst_QtConcurrentRun::nullThreadPoolNoLeak() |
763 | { |
764 | { |
765 | QThreadPool *pool = nullptr; |
766 | auto future = run(pool, functor: LifetimeChecker()); |
767 | future.waitForFinished(); |
768 | } |
769 | QCOMPARE(LifetimeChecker::count, 0); |
770 | } |
771 | |
772 | QTEST_MAIN(tst_QtConcurrentRun) |
773 | #include "tst_qtconcurrentrun.moc" |
774 | |