|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the test suite of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 #include <qtconcurrentthreadengine.h> |
|
42 #include <qtconcurrentexception.h> |
|
43 #include <QThread> |
|
44 #include <QtTest/QtTest> |
|
45 #include "../qfuture/versioncheck.h" |
|
46 |
|
47 #ifndef QT_NO_CONCURRENT_TEST |
|
48 |
|
49 using namespace QtConcurrent; |
|
50 |
|
51 class tst_threadengine: public QObject |
|
52 { |
|
53 Q_OBJECT |
|
54 public: |
|
55 void threadCount(); |
|
56 private slots: |
|
57 void runDirectly(); |
|
58 void result(); |
|
59 void runThroughStarter(); |
|
60 void cancel(); |
|
61 void throttle(); |
|
62 void multipleResults(); |
|
63 void stresstest(); |
|
64 void cancelQueuedSlowUser(); |
|
65 #ifndef QT_NO_EXCEPTIONS |
|
66 void exceptions(); |
|
67 #endif |
|
68 }; |
|
69 |
|
70 |
|
71 class PrintUser : public ThreadEngine<void> |
|
72 { |
|
73 public: |
|
74 ThreadFunctionResult threadFunction() |
|
75 { |
|
76 QTest::qSleep(50); |
|
77 QTest::qSleep(100); |
|
78 return ThreadFinished; |
|
79 } |
|
80 }; |
|
81 |
|
82 void tst_threadengine::runDirectly() |
|
83 { |
|
84 { |
|
85 PrintUser engine; |
|
86 engine.startSingleThreaded(); |
|
87 engine.startBlocking(); |
|
88 } |
|
89 { |
|
90 PrintUser *engine = new PrintUser(); |
|
91 QFuture<void> f = engine->startAsynchronously(); |
|
92 f.waitForFinished(); |
|
93 } |
|
94 } |
|
95 |
|
96 class StringResultUser : public ThreadEngine<QString> |
|
97 { |
|
98 public: |
|
99 typedef QString ResultType; |
|
100 StringResultUser() |
|
101 : done(false) { } |
|
102 |
|
103 bool shouldStartThread() |
|
104 { |
|
105 return !done; |
|
106 } |
|
107 |
|
108 ThreadFunctionResult threadFunction() |
|
109 { |
|
110 done = true; |
|
111 return ThreadFinished; |
|
112 } |
|
113 |
|
114 QString *result() |
|
115 { |
|
116 foo = "Foo"; |
|
117 return &foo; |
|
118 } |
|
119 QString foo; |
|
120 bool done; |
|
121 }; |
|
122 |
|
123 void tst_threadengine::result() |
|
124 { |
|
125 StringResultUser engine; |
|
126 QCOMPARE(*engine.startBlocking(), QString("Foo")); |
|
127 } |
|
128 |
|
129 class VoidResultUser : public ThreadEngine<void> |
|
130 { |
|
131 public: |
|
132 bool shouldStartThread() |
|
133 { |
|
134 return !done; |
|
135 } |
|
136 |
|
137 ThreadFunctionResult threadFunction() |
|
138 { |
|
139 done = true; |
|
140 return ThreadFinished; |
|
141 } |
|
142 |
|
143 void *result() |
|
144 { |
|
145 return 0; |
|
146 } |
|
147 bool done; |
|
148 }; |
|
149 |
|
150 void tst_threadengine::runThroughStarter() |
|
151 { |
|
152 { |
|
153 ThreadEngineStarter<QString> starter = startThreadEngine(new StringResultUser()); |
|
154 QFuture<QString> f = starter.startAsynchronously(); |
|
155 QCOMPARE(f.result(), QString("Foo")); |
|
156 } |
|
157 |
|
158 { |
|
159 ThreadEngineStarter<QString> starter = startThreadEngine(new StringResultUser()); |
|
160 QString str = starter.startBlocking(); |
|
161 QCOMPARE(str, QString("Foo")); |
|
162 } |
|
163 } |
|
164 |
|
165 class CancelUser : public ThreadEngine<void> |
|
166 { |
|
167 public: |
|
168 void *result() |
|
169 { |
|
170 return 0; |
|
171 } |
|
172 |
|
173 ThreadFunctionResult threadFunction() |
|
174 { |
|
175 while (this->isCanceled() == false) |
|
176 { |
|
177 QTest::qSleep(10); |
|
178 } |
|
179 return ThreadFinished; |
|
180 } |
|
181 }; |
|
182 |
|
183 void tst_threadengine::cancel() |
|
184 { |
|
185 { |
|
186 CancelUser *engine = new CancelUser(); |
|
187 QFuture<void> f = engine->startAsynchronously(); |
|
188 f.cancel(); |
|
189 f.waitForFinished(); |
|
190 } |
|
191 { |
|
192 CancelUser *engine = new CancelUser(); |
|
193 QFuture<void> f = engine->startAsynchronously(); |
|
194 QTest::qSleep(10); |
|
195 f.cancel(); |
|
196 f.waitForFinished(); |
|
197 } |
|
198 } |
|
199 |
|
200 QAtomicInt count; |
|
201 class ThrottleAlwaysUser : public ThreadEngine<void> |
|
202 { |
|
203 public: |
|
204 ThrottleAlwaysUser() |
|
205 { |
|
206 count = initialCount = 100; |
|
207 finishing = false; |
|
208 } |
|
209 |
|
210 bool shouldStartThread() |
|
211 { |
|
212 return !finishing; |
|
213 } |
|
214 |
|
215 ThreadFunctionResult threadFunction() |
|
216 { |
|
217 forever { |
|
218 const int local = count; |
|
219 if (local == 0) { |
|
220 finishing = true; |
|
221 return ThreadFinished; |
|
222 } |
|
223 |
|
224 if (count.testAndSetOrdered(local, local - 1)) |
|
225 break; |
|
226 } |
|
227 return ThrottleThread; |
|
228 } |
|
229 |
|
230 bool finishing; |
|
231 int initialCount; |
|
232 }; |
|
233 |
|
234 // Test that a user task with a thread function that always |
|
235 // want to be throttled still completes. The thread engine |
|
236 // should make keep one thread running at all times. |
|
237 void tst_threadengine::throttle() |
|
238 { |
|
239 const int repeats = 10; |
|
240 for (int i = 0; i < repeats; ++i) { |
|
241 QFuture<void> f = (new ThrottleAlwaysUser())->startAsynchronously(); |
|
242 f.waitForFinished(); |
|
243 QCOMPARE(int(count), 0); |
|
244 } |
|
245 |
|
246 for (int i = 0; i < repeats; ++i) { |
|
247 ThrottleAlwaysUser t; |
|
248 t.startBlocking(); |
|
249 QCOMPARE(int(count), 0); |
|
250 } |
|
251 } |
|
252 |
|
253 QSet<QThread *> threads; |
|
254 QMutex mutex; |
|
255 class ThreadCountUser : public ThreadEngine<void> |
|
256 { |
|
257 public: |
|
258 ThreadCountUser(bool finishImmediately = false) |
|
259 { |
|
260 threads.clear(); |
|
261 finishing = finishImmediately; |
|
262 } |
|
263 |
|
264 bool shouldStartThread() |
|
265 { |
|
266 return !finishing; |
|
267 } |
|
268 |
|
269 ThreadFunctionResult threadFunction() |
|
270 { |
|
271 { |
|
272 QMutexLocker lock(&mutex); |
|
273 threads.insert(QThread::currentThread()); |
|
274 } |
|
275 QTest::qSleep(10); |
|
276 finishing = true; |
|
277 return ThreadFinished; |
|
278 } |
|
279 |
|
280 bool finishing; |
|
281 }; |
|
282 |
|
283 void tst_threadengine::threadCount() |
|
284 { |
|
285 const int repeats = 10; |
|
286 for (int i = 0; i < repeats; ++i) { |
|
287 ThreadCountUser t; |
|
288 t.startBlocking(); |
|
289 QCOMPARE(threads.count(), QThreadPool::globalInstance()->maxThreadCount() + 1); // +1 for the main thread. |
|
290 |
|
291 (new ThreadCountUser())->startAsynchronously().waitForFinished(); |
|
292 QCOMPARE(threads.count(), QThreadPool::globalInstance()->maxThreadCount()); |
|
293 } |
|
294 |
|
295 // Set the finish flag immediately, this should give us one thread only. |
|
296 for (int i = 0; i < repeats; ++i) { |
|
297 ThreadCountUser t(true /*finishImmediately*/); |
|
298 t.startBlocking(); |
|
299 QCOMPARE(threads.count(), 1); |
|
300 |
|
301 (new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished(); |
|
302 QCOMPARE(threads.count(), 1); |
|
303 } |
|
304 } |
|
305 |
|
306 class MultipleResultsUser : public ThreadEngine<int> |
|
307 { |
|
308 public: |
|
309 bool shouldStartThread() |
|
310 { |
|
311 return false; |
|
312 } |
|
313 |
|
314 ThreadFunctionResult threadFunction() |
|
315 { |
|
316 for (int i = 0; i < 10; ++i) |
|
317 this->reportResult(&i); |
|
318 return ThreadFinished; |
|
319 } |
|
320 }; |
|
321 |
|
322 |
|
323 void tst_threadengine::multipleResults() |
|
324 { |
|
325 MultipleResultsUser *engine = new MultipleResultsUser(); |
|
326 QFuture<int> f = engine->startAsynchronously(); |
|
327 QCOMPARE(f.results().count() , 10); |
|
328 QCOMPARE(f.resultAt(0), 0); |
|
329 QCOMPARE(f.resultAt(5), 5); |
|
330 QCOMPARE(f.resultAt(9), 9); |
|
331 f.waitForFinished(); |
|
332 } |
|
333 |
|
334 |
|
335 class NoThreadsUser : public ThreadEngine<void> |
|
336 { |
|
337 public: |
|
338 bool shouldStartThread() |
|
339 { |
|
340 return false; |
|
341 } |
|
342 |
|
343 ThreadFunctionResult threadFunction() |
|
344 { |
|
345 return ThreadFinished; |
|
346 } |
|
347 |
|
348 void *result() |
|
349 { |
|
350 return 0; |
|
351 } |
|
352 }; |
|
353 |
|
354 void tst_threadengine::stresstest() |
|
355 { |
|
356 const int times = 20000; |
|
357 |
|
358 for (int i = 0; i < times; ++i) { |
|
359 VoidResultUser *engine = new VoidResultUser(); |
|
360 engine->startAsynchronously().waitForFinished(); |
|
361 } |
|
362 |
|
363 for (int i = 0; i < times; ++i) { |
|
364 VoidResultUser *engine = new VoidResultUser(); |
|
365 engine->startAsynchronously(); |
|
366 } |
|
367 |
|
368 for (int i = 0; i < times; ++i) { |
|
369 VoidResultUser *engine = new VoidResultUser(); |
|
370 engine->startAsynchronously().waitForFinished(); |
|
371 } |
|
372 } |
|
373 |
|
374 const int sleepTime = 20; |
|
375 class SlowUser : public ThreadEngine<void> |
|
376 { |
|
377 public: |
|
378 bool shouldStartThread() { return false; } |
|
379 ThreadFunctionResult threadFunction() { QTest::qSleep(sleepTime); return ThreadFinished; } |
|
380 }; |
|
381 |
|
382 void tst_threadengine::cancelQueuedSlowUser() |
|
383 { |
|
384 const int times = 100; |
|
385 |
|
386 QTime t; |
|
387 t.start(); |
|
388 |
|
389 { |
|
390 QList<QFuture<void> > futures; |
|
391 for (int i = 0; i < times; ++i) { |
|
392 SlowUser *engine = new SlowUser(); |
|
393 futures.append(engine->startAsynchronously()); |
|
394 } |
|
395 |
|
396 foreach(QFuture<void> future, futures) |
|
397 future.cancel(); |
|
398 } |
|
399 |
|
400 QVERIFY(t.elapsed() < (sleepTime * times) / 2); |
|
401 } |
|
402 |
|
403 #ifndef QT_NO_EXCEPTIONS |
|
404 |
|
405 class QtConcurrentExceptionThrower : public ThreadEngine<void> |
|
406 { |
|
407 public: |
|
408 QtConcurrentExceptionThrower(QThread *blockThread = 0) |
|
409 { |
|
410 this->blockThread = blockThread; |
|
411 } |
|
412 |
|
413 ThreadFunctionResult threadFunction() |
|
414 { |
|
415 QTest::qSleep(50); |
|
416 throw QtConcurrent::Exception(); |
|
417 return ThreadFinished; |
|
418 } |
|
419 QThread *blockThread; |
|
420 }; |
|
421 |
|
422 class UnrelatedExceptionThrower : public ThreadEngine<void> |
|
423 { |
|
424 public: |
|
425 UnrelatedExceptionThrower(QThread *blockThread = 0) |
|
426 { |
|
427 this->blockThread = blockThread; |
|
428 } |
|
429 |
|
430 ThreadFunctionResult threadFunction() |
|
431 { |
|
432 QTest::qSleep(50); |
|
433 throw int(); |
|
434 return ThreadFinished; |
|
435 } |
|
436 QThread *blockThread; |
|
437 }; |
|
438 |
|
439 void tst_threadengine::exceptions() |
|
440 { |
|
441 // Asynchronous mode: |
|
442 { |
|
443 bool caught = false; |
|
444 try { |
|
445 QtConcurrentExceptionThrower *e = new QtConcurrentExceptionThrower(); |
|
446 QFuture<void> f = e->startAsynchronously(); |
|
447 f.waitForFinished(); |
|
448 } catch (Exception &e) { |
|
449 caught = true; |
|
450 } |
|
451 if (!caught) |
|
452 QFAIL("did not get exception"); |
|
453 } |
|
454 |
|
455 // Blocking mode: |
|
456 // test throwing the exception from a worker thread. |
|
457 { |
|
458 bool caught = false; |
|
459 try { |
|
460 QtConcurrentExceptionThrower e(QThread::currentThread()); |
|
461 e.startBlocking(); |
|
462 } catch (Exception &e) { |
|
463 caught = true; |
|
464 } |
|
465 |
|
466 if (!caught) |
|
467 QFAIL("did not get exception"); |
|
468 } |
|
469 |
|
470 // test throwing the exception from the main thread (different code path) |
|
471 { |
|
472 bool caught = false; |
|
473 try { |
|
474 QtConcurrentExceptionThrower e(0); |
|
475 e.startBlocking(); |
|
476 } catch (Exception &e) { |
|
477 caught = true; |
|
478 } |
|
479 |
|
480 if (!caught) |
|
481 QFAIL("did not get exception"); |
|
482 } |
|
483 |
|
484 // Asynchronous mode: |
|
485 { |
|
486 bool caught = false; |
|
487 try { |
|
488 UnrelatedExceptionThrower *e = new UnrelatedExceptionThrower(); |
|
489 QFuture<void> f = e->startAsynchronously(); |
|
490 f.waitForFinished(); |
|
491 } catch (QtConcurrent::UnhandledException &e) { |
|
492 caught = true; |
|
493 } |
|
494 if (!caught) |
|
495 QFAIL("did not get exception"); |
|
496 } |
|
497 |
|
498 // Blocking mode: |
|
499 // test throwing the exception from a worker thread. |
|
500 { |
|
501 bool caught = false; |
|
502 try { |
|
503 UnrelatedExceptionThrower e(QThread::currentThread()); |
|
504 e.startBlocking(); |
|
505 } catch (QtConcurrent::UnhandledException &e) { |
|
506 caught = true; |
|
507 } |
|
508 |
|
509 if (!caught) |
|
510 QFAIL("did not get exception"); |
|
511 } |
|
512 |
|
513 // test throwing the exception from the main thread (different code path) |
|
514 { |
|
515 bool caught = false; |
|
516 try { |
|
517 UnrelatedExceptionThrower e(0); |
|
518 e.startBlocking(); |
|
519 } catch (QtConcurrent::UnhandledException &e) { |
|
520 caught = true; |
|
521 } |
|
522 |
|
523 if (!caught) |
|
524 QFAIL("did not get exception"); |
|
525 } |
|
526 } |
|
527 |
|
528 #endif |
|
529 |
|
530 QTEST_MAIN(tst_threadengine) |
|
531 |
|
532 #include "tst_qtconcurrentthreadengine.moc" |
|
533 |
|
534 #else |
|
535 QTEST_NOOP_MAIN |
|
536 #endif |