tests/auto/qtconcurrentthreadengine/tst_qtconcurrentthreadengine.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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