/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "../qfuture/versioncheck.h" #ifndef QT_NO_CONCURRENT_TEST using namespace QtConcurrent; class tst_QtConcurrentThreadEngine: public QObject { Q_OBJECT public: void threadCount(); private slots: void runDirectly(); void result(); void runThroughStarter(); void cancel(); void throttle(); void multipleResults(); void stresstest(); void cancelQueuedSlowUser(); #ifndef QT_NO_EXCEPTIONS void exceptions(); #endif }; class PrintUser : public ThreadEngine { public: ThreadFunctionResult threadFunction() { QTest::qSleep(50); QTest::qSleep(100); return ThreadFinished; } }; void tst_QtConcurrentThreadEngine::runDirectly() { { PrintUser engine; engine.startSingleThreaded(); engine.startBlocking(); } { PrintUser *engine = new PrintUser(); QFuture f = engine->startAsynchronously(); f.waitForFinished(); } } class StringResultUser : public ThreadEngine { public: typedef QString ResultType; StringResultUser() : done(false) { } bool shouldStartThread() { return !done; } ThreadFunctionResult threadFunction() { done = true; return ThreadFinished; } QString *result() { foo = "Foo"; return &foo; } QString foo; bool done; }; void tst_QtConcurrentThreadEngine::result() { StringResultUser engine; QCOMPARE(*engine.startBlocking(), QString("Foo")); } class VoidResultUser : public ThreadEngine { public: bool shouldStartThread() { return !done; } ThreadFunctionResult threadFunction() { done = true; return ThreadFinished; } void *result() { return 0; } bool done; }; void tst_QtConcurrentThreadEngine::runThroughStarter() { { ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); QFuture f = starter.startAsynchronously(); QCOMPARE(f.result(), QString("Foo")); } { ThreadEngineStarter starter = startThreadEngine(new StringResultUser()); QString str = starter.startBlocking(); QCOMPARE(str, QString("Foo")); } } class CancelUser : public ThreadEngine { public: void *result() { return 0; } ThreadFunctionResult threadFunction() { while (this->isCanceled() == false) { QTest::qSleep(10); } return ThreadFinished; } }; void tst_QtConcurrentThreadEngine::cancel() { { CancelUser *engine = new CancelUser(); QFuture f = engine->startAsynchronously(); f.cancel(); f.waitForFinished(); } { CancelUser *engine = new CancelUser(); QFuture f = engine->startAsynchronously(); QTest::qSleep(10); f.cancel(); f.waitForFinished(); } } QAtomicInt count; class ThrottleAlwaysUser : public ThreadEngine { public: ThrottleAlwaysUser() { count = initialCount = 100; finishing = false; } bool shouldStartThread() { return !finishing; } ThreadFunctionResult threadFunction() { forever { const int local = count; if (local == 0) { finishing = true; return ThreadFinished; } if (count.testAndSetOrdered(local, local - 1)) break; } return ThrottleThread; } bool finishing; int initialCount; }; // Test that a user task with a thread function that always // want to be throttled still completes. The thread engine // should make keep one thread running at all times. void tst_QtConcurrentThreadEngine::throttle() { const int repeats = 10; for (int i = 0; i < repeats; ++i) { QFuture f = (new ThrottleAlwaysUser())->startAsynchronously(); f.waitForFinished(); QCOMPARE(int(count), 0); } for (int i = 0; i < repeats; ++i) { ThrottleAlwaysUser t; t.startBlocking(); QCOMPARE(int(count), 0); } } QSet threads; QMutex mutex; class ThreadCountUser : public ThreadEngine { public: ThreadCountUser(bool finishImmediately = false) { threads.clear(); finishing = finishImmediately; } bool shouldStartThread() { return !finishing; } ThreadFunctionResult threadFunction() { { QMutexLocker lock(&mutex); threads.insert(QThread::currentThread()); } QTest::qSleep(10); finishing = true; return ThreadFinished; } bool finishing; }; void tst_QtConcurrentThreadEngine::threadCount() { const int repeats = 10; for (int i = 0; i < repeats; ++i) { ThreadCountUser t; t.startBlocking(); QCOMPARE(threads.count(), QThreadPool::globalInstance()->maxThreadCount() + 1); // +1 for the main thread. (new ThreadCountUser())->startAsynchronously().waitForFinished(); QCOMPARE(threads.count(), QThreadPool::globalInstance()->maxThreadCount()); } // Set the finish flag immediately, this should give us one thread only. for (int i = 0; i < repeats; ++i) { ThreadCountUser t(true /*finishImmediately*/); t.startBlocking(); QCOMPARE(threads.count(), 1); (new ThreadCountUser(true /*finishImmediately*/))->startAsynchronously().waitForFinished(); QCOMPARE(threads.count(), 1); } } class MultipleResultsUser : public ThreadEngine { public: bool shouldStartThread() { return false; } ThreadFunctionResult threadFunction() { for (int i = 0; i < 10; ++i) this->reportResult(&i); return ThreadFinished; } }; void tst_QtConcurrentThreadEngine::multipleResults() { MultipleResultsUser *engine = new MultipleResultsUser(); QFuture f = engine->startAsynchronously(); QCOMPARE(f.results().count() , 10); QCOMPARE(f.resultAt(0), 0); QCOMPARE(f.resultAt(5), 5); QCOMPARE(f.resultAt(9), 9); f.waitForFinished(); } class NoThreadsUser : public ThreadEngine { public: bool shouldStartThread() { return false; } ThreadFunctionResult threadFunction() { return ThreadFinished; } void *result() { return 0; } }; void tst_QtConcurrentThreadEngine::stresstest() { const int times = 20000; for (int i = 0; i < times; ++i) { VoidResultUser *engine = new VoidResultUser(); engine->startAsynchronously().waitForFinished(); } for (int i = 0; i < times; ++i) { VoidResultUser *engine = new VoidResultUser(); engine->startAsynchronously(); } for (int i = 0; i < times; ++i) { VoidResultUser *engine = new VoidResultUser(); engine->startAsynchronously().waitForFinished(); } } const int sleepTime = 20; class SlowUser : public ThreadEngine { public: bool shouldStartThread() { return false; } ThreadFunctionResult threadFunction() { QTest::qSleep(sleepTime); return ThreadFinished; } }; void tst_QtConcurrentThreadEngine::cancelQueuedSlowUser() { const int times = 100; QTime t; t.start(); { QList > futures; for (int i = 0; i < times; ++i) { SlowUser *engine = new SlowUser(); futures.append(engine->startAsynchronously()); } foreach(QFuture future, futures) future.cancel(); } QVERIFY(t.elapsed() < (sleepTime * times) / 2); } #ifndef QT_NO_EXCEPTIONS class QtConcurrentExceptionThrower : public ThreadEngine { public: QtConcurrentExceptionThrower(QThread *blockThread = 0) { this->blockThread = blockThread; } ThreadFunctionResult threadFunction() { QTest::qSleep(50); throw QtConcurrent::Exception(); return ThreadFinished; } QThread *blockThread; }; class UnrelatedExceptionThrower : public ThreadEngine { public: UnrelatedExceptionThrower(QThread *blockThread = 0) { this->blockThread = blockThread; } ThreadFunctionResult threadFunction() { QTest::qSleep(50); throw int(); return ThreadFinished; } QThread *blockThread; }; void tst_QtConcurrentThreadEngine::exceptions() { // Asynchronous mode: { bool caught = false; try { QtConcurrentExceptionThrower *e = new QtConcurrentExceptionThrower(); QFuture f = e->startAsynchronously(); f.waitForFinished(); } catch (Exception &e) { caught = true; } if (!caught) QFAIL("did not get exception"); } // Blocking mode: // test throwing the exception from a worker thread. { bool caught = false; try { QtConcurrentExceptionThrower e(QThread::currentThread()); e.startBlocking(); } catch (Exception &e) { caught = true; } if (!caught) QFAIL("did not get exception"); } // test throwing the exception from the main thread (different code path) { bool caught = false; try { QtConcurrentExceptionThrower e(0); e.startBlocking(); } catch (Exception &e) { caught = true; } if (!caught) QFAIL("did not get exception"); } // Asynchronous mode: { bool caught = false; try { UnrelatedExceptionThrower *e = new UnrelatedExceptionThrower(); QFuture f = e->startAsynchronously(); f.waitForFinished(); } catch (QtConcurrent::UnhandledException &e) { caught = true; } if (!caught) QFAIL("did not get exception"); } // Blocking mode: // test throwing the exception from a worker thread. { bool caught = false; try { UnrelatedExceptionThrower e(QThread::currentThread()); e.startBlocking(); } catch (QtConcurrent::UnhandledException &e) { caught = true; } if (!caught) QFAIL("did not get exception"); } // test throwing the exception from the main thread (different code path) { bool caught = false; try { UnrelatedExceptionThrower e(0); e.startBlocking(); } catch (QtConcurrent::UnhandledException &e) { caught = true; } if (!caught) QFAIL("did not get exception"); } } #endif QTEST_MAIN(tst_QtConcurrentThreadEngine) #include "tst_qtconcurrentthreadengine.moc" #else QTEST_NOOP_MAIN #endif