diff --git a/autotests/AppendCharacterJob.h b/autotests/AppendCharacterJob.h index b12df8e..fdd83e0 100644 --- a/autotests/AppendCharacterJob.h +++ b/autotests/AppendCharacterJob.h @@ -1,125 +1,125 @@ /* -*- C++ -*- Helper class for unit tests. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef APPENDCHARACTER_JOB #define APPENDCHARACTER_JOB #include #include #include #include #include // define in test binary: extern QMutex s_GlobalMutex; class AppendCharacterJob : public ThreadWeaver::Job { public: - AppendCharacterJob(QChar c = QChar(), QString *stringref = 0) + AppendCharacterJob(QChar c = QChar(), QString *stringref = nullptr) : ThreadWeaver::Job() { setValues(c, stringref); } void setValues(QChar c, QString *stringref) { m_c = c; m_stringref = stringref; } void run(ThreadWeaver::JobPointer, ThreadWeaver::Thread *) Q_DECL_OVERRIDE { QMutexLocker locker(&s_GlobalMutex); m_stringref->append(m_c); using namespace ThreadWeaver; TWDEBUG(3, "AppendCharacterJob::run: %c appended, result is %s.\n", m_c.toLatin1(), qPrintable(*m_stringref)); } QChar character() const { return m_c; } protected: QString *stringRef() const { return m_stringref; } private: QChar m_c; QString *m_stringref; }; class FailingAppendCharacterJob : public AppendCharacterJob { public: - FailingAppendCharacterJob(QChar c = QChar(), QString *stringref = 0) + FailingAppendCharacterJob(QChar c = QChar(), QString *stringref = nullptr) : AppendCharacterJob(c, stringref) { } void run(ThreadWeaver::JobPointer job, ThreadWeaver::Thread* thread) Q_DECL_OVERRIDE { AppendCharacterJob::run(job, thread); setStatus(Job::Status_Failed); } }; class BusyJob : public ThreadWeaver::Job { public: BusyJob() : ThreadWeaver::Job() { using namespace ThreadWeaver; TWDEBUG(3, "BusyJob ctor\n"); } ~BusyJob() { using namespace ThreadWeaver; TWDEBUG(3, "~BusyJob\n"); } void run(ThreadWeaver::JobPointer, ThreadWeaver::Thread *) Q_DECL_OVERRIDE { using namespace ThreadWeaver; TWDEBUG(3, "BusyJob: entered run()\n"); for (int i = 0; i < 100; ++i) { int k = (i << 3) + (i >> 4); Q_UNUSED(k); } } }; #endif diff --git a/autotests/JobLoggingWeaver.h b/autotests/JobLoggingWeaver.h index 2bc7a4f..d9ac3a2 100644 --- a/autotests/JobLoggingWeaver.h +++ b/autotests/JobLoggingWeaver.h @@ -1,46 +1,46 @@ /* -*- C++ -*- This file is part of ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef JOBLOGGINGWEAVER_H #define JOBLOGGINGWEAVER_H #include "src/weaver.h" #include "src/jobpointer.h" #include "JobLoggingDecorator.h" class JobLoggingWeaver : public ThreadWeaver::Weaver { Q_OBJECT public: - explicit JobLoggingWeaver(QObject* parent = 0); + explicit JobLoggingWeaver(QObject* parent = nullptr); void enqueue(const QVector &jobs) Q_DECL_OVERRIDE; private: JobLoggingDecoratorCollector collector_; }; #endif // JOBLOGGINGWEAVER_H diff --git a/autotests/JobTests.cpp b/autotests/JobTests.cpp index 5ac0de5..aaa88d5 100644 --- a/autotests/JobTests.cpp +++ b/autotests/JobTests.cpp @@ -1,1212 +1,1212 @@ /* -*- C++ -*- This file contains a testsuite for job processing in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "JobTests.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AppendCharacterJob.h" #include "AppendCharacterAndVerifyJob.h" #include "WaitForIdleAndFinished.h" #include "JobLoggingWeaver.h" QMutex s_GlobalMutex; using namespace ThreadWeaver; void JobTests::initTestCase() { setDebugLevel(true, 1); } // Call finish() before leaving a test or use a WaitForIdleAndFinished object to make sure the queue is empty // and in an idle state. void JobTests::WeaverLazyThreadCreationTest() { Queue weaver; QString sequence; WaitForIdleAndFinished w(&weaver); Q_ASSERT(weaver.isIdle()); QCOMPARE(weaver.currentNumberOfThreads(), 0); weaver.setMaximumNumberOfThreads(1); weaver.stream() << new AppendCharacterJob(QChar('a'), &sequence); weaver.finish(); QCOMPARE(weaver.currentNumberOfThreads(), 1); Q_ASSERT(weaver.isIdle()); } void JobTests::ReduceWorkerCountTest() { Queue weaver; QString sequence; WaitForIdleAndFinished w(&weaver); Q_ASSERT(weaver.isIdle()); QCOMPARE(weaver.currentNumberOfThreads(), 0); weaver.setMaximumNumberOfThreads(8); weaver.stream() << new AppendCharacterJob(QChar('a'), &sequence); weaver.finish(); QVERIFY(weaver.currentNumberOfThreads() >= 1); weaver.setMaximumNumberOfThreads(1); weaver.stream() << new AppendCharacterJob(QChar('b'), &sequence); weaver.finish(); QCOMPARE(weaver.currentNumberOfThreads(), 1); weaver.setMaximumNumberOfThreads(0); weaver.stream() << new AppendCharacterJob(QChar('c'), &sequence); weaver.reschedule(); // Unfortunately, there is no way to enforce that the last thread has exited: // QCOMPARE(weaver.currentNumberOfThreads(), 0); weaver.setMaximumNumberOfThreads(1); weaver.stream() << new AppendCharacterJob(QChar('d'), &sequence); weaver.finish(); QCOMPARE(weaver.currentNumberOfThreads(), 1); QCOMPARE(sequence, QLatin1String("abcd")); } void JobTests::SimpleJobTest() { QString sequence; WaitForIdleAndFinished w(Queue::instance()); stream() << new AppendCharacterJob(QChar('1'), &sequence); Queue::instance()->finish(); QCOMPARE(sequence, QString("1")); } void JobTests::SimpleJobCollectionTest() { QString sequence; Collection jobCollection; jobCollection << new AppendCharacterJob(QChar('a'), &sequence) << new AppendCharacterJob(QChar('b'), &sequence) << new AppendCharacterJob(QChar('c'), &sequence); WaitForIdleAndFinished w(Queue::instance()); stream() << jobCollection; Queue::instance()->finish(); QVERIFY(sequence.length() == 3); QVERIFY(sequence.count('a') == 1); QVERIFY(sequence.count('b') == 1); QVERIFY(sequence.count('c') == 1); } void JobTests::EmptyJobCollectionTest() { Collection collection; WaitForIdleAndFinished w(Queue::instance()); Q_ASSERT(Queue::instance()->isIdle()); stream() << collection; Queue::instance()->finish(); QVERIFY(collection.isFinished()); QVERIFY(Queue::instance()->isIdle()); } void JobTests::CollectionQueueingTest() { QString output; Collection jobCollection; jobCollection << new AppendCharacterJob(QChar('a'), &output) << new AppendCharacterJob(QChar('b'), &output) << new AppendCharacterJob(QChar('c'), &output); Queue weaver; WaitForIdleAndFinished w(&weaver); weaver.suspend(); weaver.stream() << jobCollection; QCOMPARE(weaver.queueLength(), 1); //collection queues itself, and its elements upon execution of self weaver.resume(); weaver.finish(); QCOMPARE(output.length(), 3); QVERIFY(Queue::instance()->isIdle()); } namespace { using namespace ThreadWeaver; QString SequenceTemplate = "abcdefghijklmnopqrstuvwxyz"; class GeneratingCollection : public Collection { public: void run(JobPointer, Thread *) Q_DECL_OVERRIDE { std::for_each(SequenceTemplate.cbegin(), SequenceTemplate.cend(), [this](QChar it) { *this << new AppendCharacterJob(it, &sequence_); } ); } QString sequence_; }; class GeneratingSequence : public Sequence { public: void run(JobPointer, Thread *) Q_DECL_OVERRIDE { std::for_each(SequenceTemplate.cbegin(), SequenceTemplate.cend(), [this](QChar it) { *this << new AppendCharacterJob(it, &sequence_); } ); } QString sequence_; }; } void JobTests::GeneratingCollectionTest() { using namespace ThreadWeaver; GeneratingCollection collection; WaitForIdleAndFinished w(Queue::instance()); stream() << collection; Queue::instance()->finish(); QCOMPARE(collection.sequence_.count(), SequenceTemplate.length()); } void JobTests::ShortJobSequenceTest() { QString sequence; Sequence jobSequence; jobSequence << new AppendCharacterJob(QChar('a'), &sequence) << new AppendCharacterJob(QChar('b'), &sequence) << new AppendCharacterJob(QChar('c'), &sequence); WaitForIdleAndFinished w(Queue::instance()); QVERIFY(DependencyPolicy::instance().isEmpty()); stream() << jobSequence; Queue::instance()->finish(); QCOMPARE(sequence, QLatin1String("abc")); QVERIFY(Queue::instance()->isIdle()); QVERIFY(DependencyPolicy::instance().isEmpty()); } void JobTests::ShortDecoratedJobSequenceTest() { using namespace ThreadWeaver; auto logger = new JobLoggingWeaver(); Queue queue(logger); QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); QSharedPointer jobSequence(new Sequence()); jobSequence->addJob(jobA); jobSequence->addJob(jobB); jobSequence->addJob(jobC); WaitForIdleAndFinished w(&queue); Q_UNUSED(w); QVERIFY(DependencyPolicy::instance().isEmpty()); queue.enqueue(jobSequence); // Job::DumpJobDependencies(); queue.finish(); QCOMPARE(sequence, QLatin1String("abc")); QVERIFY(queue.isIdle()); QVERIFY(DependencyPolicy::instance().isEmpty()); } void JobTests::EmptyJobSequenceTest() { using namespace ThreadWeaver; QObjectDecorator sequence(new Sequence()); WaitForIdleAndFinished w(Queue::instance()); Q_UNUSED(w); Q_ASSERT(Queue::instance()->isIdle()); QSignalSpy doneSignalSpy(&sequence, SIGNAL(done(ThreadWeaver::JobPointer))); QCOMPARE(doneSignalSpy.count(), 0); enqueue_raw(&sequence); Queue::instance()->finish(); QVERIFY(sequence.isFinished()); QVERIFY(Queue::instance()->isIdle()); QCOMPARE(doneSignalSpy.count(), 1); } void JobTests::GeneratingSequenceTest() { using namespace ThreadWeaver; GeneratingSequence sequence; WaitForIdleAndFinished w(Queue::instance()); stream() << make_job_raw(&sequence); Queue::instance()->finish(); QCOMPARE(sequence.sequence_, SequenceTemplate); } /** This test verifies that the done signal for a collection is only sent after all element of the collection have completed. */ void JobTests::IncompleteCollectionTest() { using namespace ThreadWeaver; QString result; QObjectDecorator jobA(new AppendCharacterJob(QChar('a'), &result)); AppendCharacterJob jobB(QChar('b'), &result); //jobB does not get added to the sequence and queued QObjectDecorator col(new Collection()); *col.collection() << jobA; WaitForIdleAndFinished w(Queue::instance()); DependencyPolicy::instance().addDependency(Dependency(&jobA, &jobB)); QSignalSpy collectionDoneSignalSpy(&col, SIGNAL(done(ThreadWeaver::JobPointer))); QSignalSpy jobADoneSignalSpy(&jobA, SIGNAL(done(ThreadWeaver::JobPointer))); QCOMPARE(collectionDoneSignalSpy.count(), 0); QCOMPARE(jobADoneSignalSpy.count(), 0); enqueue_raw(&col); Queue::instance()->resume(); QCoreApplication::processEvents(); QCOMPARE(collectionDoneSignalSpy.count(), 0); QCOMPARE(jobADoneSignalSpy.count(), 0); DependencyPolicy::instance().removeDependency(Dependency(&jobA, &jobB)); Queue::instance()->finish(); QCoreApplication::processEvents(); QVERIFY(col.collection()->isFinished()); QVERIFY(Queue::instance()->isIdle()); QCOMPARE(collectionDoneSignalSpy.count(), 1); QCOMPARE(jobADoneSignalSpy.count(), 1); } /** This test verifies that started() is emitted for a collection at the time the first of any elements of the collection gets * executed. */ void JobTests::EmitStartedOnFirstElementTest() { using namespace ThreadWeaver; WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); QString result; JobPointer jobA(new AppendCharacterJob(QChar('a'), &result)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &result)); QObjectDecorator collection(new Collection()); Collection *decorated = dynamic_cast(collection.job()); - QVERIFY(decorated != 0); + QVERIFY(decorated != nullptr); decorated->addJob(jobA); decorated->addJob(jobB); enqueue(make_job_raw(&collection)); QSignalSpy collectionStartedSignalSpy(&collection, SIGNAL(started(ThreadWeaver::JobPointer))); QSignalSpy collectionDoneSignalSpy(&collection, SIGNAL(done(ThreadWeaver::JobPointer))); Queue::instance()->resume(); QCoreApplication::processEvents(); Queue::instance()->finish(); QVERIFY(collection.isFinished()); QCOMPARE(result.length(), 2); for (int i = 0; i < 100; ++i) { if (collectionStartedSignalSpy.count() != 0 && collectionDoneSignalSpy.count() != 0) { break; } QTest::qWait(1); TWDEBUG(2, "JobTests::EmitStartedOnFirstElementTest: waiting (%i)\n", i); qApp->processEvents(); } QCOMPARE(collectionStartedSignalSpy.count(), 1); QCOMPARE(collectionDoneSignalSpy.count(), 1); QVERIFY(Queue::instance()->isIdle()); } /* This test verifies that all elements of a collection are only executed after all dependencies for the collection * itself have been resolved. * Previous tests have already verified that collections without dependencies get executed right away. */ void JobTests::CollectionDependenciesTest() { using namespace ThreadWeaver; QString result; // set up a collection that depends on jobC which does not get queued JobPointer jobA(new AppendCharacterJob(QChar('a'), &result)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &result)); QObjectDecorator col(new Collection()); QSignalSpy collectionStartedSignalSpy(&col, SIGNAL(started(ThreadWeaver::JobPointer))); col.collection()->addJob(jobA); col.collection()->addJob(jobB); QEventLoop loop; connect(&col, SIGNAL(started(ThreadWeaver::JobPointer)), &loop, SLOT(quit())); QSharedPointer jobC(new AppendCharacterJob(QChar('c'), &result)); DependencyPolicy::instance().addDependency(Dependency(&col, jobC)); // queue collection, but not jobC, the collection should not be executed WaitForIdleAndFinished w(Queue::instance()); Q_UNUSED(w); Queue::instance()->suspend(); enqueue_raw(&col); Queue::instance()->resume(); QCoreApplication::processEvents(); QTest::qWait(100); //FIXME verify: dfaure needed this here: QTRY_COMPARE(collectionStartedSignalSpy.count(), 0); QCOMPARE(collectionStartedSignalSpy.count(), 0); // enqueue jobC, first jobC then the collection should be executed Queue::instance()->enqueue(jobC); QCoreApplication::processEvents(); Queue::instance()->finish(); QVERIFY(col.isFinished()); QVERIFY(result.startsWith(jobC->character())); //QSKIP("This test is too fragile"); // PENDING(Mirko): fix //QTRY_COMPARE(collectionStartedSignalSpy.count(), 1); loop.exec(); qApp->processEvents(); QCOMPARE(collectionStartedSignalSpy.count(), 1); QVERIFY(Queue::instance()->isIdle()); } void JobTests::QueueAndDequeueCollectionTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); QSharedPointer collection(new Collection()); collection->addJob(jobA); collection->addJob(jobB); collection->addJob(jobC); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(collection); Queue::instance()->dequeue(collection); QVERIFY(Queue::instance()->isEmpty()); } void JobTests::QueueAndDequeueSequenceTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); QSharedPointer jobSequence(new Sequence()); jobSequence->addJob(jobA); jobSequence->addJob(jobB); jobSequence->addJob(jobC); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(jobSequence); Queue::instance()->dequeue(jobSequence); QVERIFY(Queue::instance()->isEmpty()); } void JobTests::BlockingExecuteTest() { QString sequence; AppendCharacterJob job(QChar('a'), &sequence); job.blockingExecute(); QCOMPARE(sequence, QString("a")); } void JobTests::RecursiveSequenceTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); JobPointer jobD(new AppendCharacterJob(QChar('d'), &sequence)); JobPointer jobE(new AppendCharacterJob(QChar('e'), &sequence)); JobPointer jobF(new AppendCharacterJob(QChar('f'), &sequence)); JobPointer jobG(new AppendCharacterJob(QChar('g'), &sequence)); JobPointer jobH(new AppendCharacterJob(QChar('h'), &sequence)); JobPointer jobI(new AppendCharacterJob(QChar('i'), &sequence)); JobPointer jobJ(new AppendCharacterJob(QChar('j'), &sequence)); QSharedPointer jobSequence1(new Sequence()); jobSequence1->addJob(jobA); jobSequence1->addJob(jobB); jobSequence1->addJob(jobC); QSharedPointer jobSequence2(new Sequence()); jobSequence2->addJob(jobD); jobSequence2->addJob(jobE); jobSequence2->addJob(jobF); QSharedPointer jobSequence3(new Sequence()); jobSequence3->addJob(jobG); jobSequence3->addJob(jobH); jobSequence3->addJob(jobI); jobSequence3->addJob(jobJ); // sequence 4 will contain sequences 1, 2, and 3, in that order: QSharedPointer jobSequence4(new Sequence()); jobSequence4->addJob(jobSequence1); jobSequence4->addJob(jobSequence2); jobSequence4->addJob(jobSequence3); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(jobSequence4); // DependencyPolicy::instance().dumpJobDependencies(); Queue::instance()->resume(); Queue::instance()->finish(); QCOMPARE(sequence, QLatin1String("abcdefghij")); } void JobTests::RecursiveQueueAndDequeueCollectionTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); JobPointer jobD(new AppendCharacterJob(QChar('d'), &sequence)); JobPointer jobE(new AppendCharacterJob(QChar('e'), &sequence)); JobPointer jobF(new AppendCharacterJob(QChar('f'), &sequence)); JobPointer jobG(new AppendCharacterJob(QChar('g'), &sequence)); JobPointer jobH(new AppendCharacterJob(QChar('h'), &sequence)); JobPointer jobI(new AppendCharacterJob(QChar('i'), &sequence)); JobPointer jobJ(new AppendCharacterJob(QChar('j'), &sequence)); QSharedPointer collection1(new Collection()); collection1->addJob(jobA); collection1->addJob(jobB); collection1->addJob(jobC); QSharedPointer collection2(new Collection()); collection2->addJob(jobD); collection2->addJob(jobE); collection2->addJob(jobF); QSharedPointer collection3(new Collection()); collection3->addJob(jobG); collection3->addJob(jobH); collection3->addJob(jobI); collection3->addJob(jobJ); // sequence 4 will contain sequences 1, 2, and 3, in that order: QSharedPointer collection4(new Collection()); collection4->addJob(collection1); collection4->addJob(collection2); collection4->addJob(collection3); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(collection4); Queue::instance()->dequeue(collection4); QVERIFY(Queue::instance()->isEmpty()); Queue::instance()->resume(); } void JobTests::RecursiveQueueAndDequeueSequenceTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); JobPointer jobD(new AppendCharacterJob(QChar('d'), &sequence)); JobPointer jobE(new AppendCharacterJob(QChar('e'), &sequence)); JobPointer jobF(new AppendCharacterJob(QChar('f'), &sequence)); JobPointer jobG(new AppendCharacterJob(QChar('g'), &sequence)); JobPointer jobH(new AppendCharacterJob(QChar('h'), &sequence)); JobPointer jobI(new AppendCharacterJob(QChar('i'), &sequence)); JobPointer jobJ(new AppendCharacterJob(QChar('j'), &sequence)); QSharedPointer jobSequence1(new Sequence()); jobSequence1->addJob(jobA); jobSequence1->addJob(jobB); jobSequence1->addJob(jobC); QSharedPointer jobSequence2(new Sequence()); jobSequence2->addJob(jobD); jobSequence2->addJob(jobE); jobSequence2->addJob(jobF); QSharedPointer jobSequence3(new Sequence()); jobSequence3->addJob(jobG); jobSequence3->addJob(jobH); jobSequence3->addJob(jobI); jobSequence3->addJob(jobJ); // sequence 4 will contain sequences 1, 2, and 3, in that order: QSharedPointer jobSequence4(new Sequence()); jobSequence4->addJob(jobSequence1); jobSequence4->addJob(jobSequence2); jobSequence4->addJob(jobSequence3); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(jobSequence4); Queue::instance()->dequeue(jobSequence4); QVERIFY(Queue::instance()->isEmpty()); Queue::instance()->resume(); } void JobTests::QueueAndDequeueAllCollectionTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); QSharedPointer collection(new Collection()); collection->addJob(jobA); collection->addJob(jobB); collection->addJob(jobC); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); QVERIFY(Queue::instance()->isEmpty()); Queue::instance()->enqueue(collection); //collection cannot have been started, so only one job is queued at the moment: QCOMPARE(Queue::instance()->queueLength(), 1); Queue::instance()->dequeue(); QVERIFY(Queue::instance()->isEmpty()); } void JobTests::QueueAndDequeueAllSequenceTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); QSharedPointer jobSequence(new Sequence()); jobSequence->addJob(jobA); jobSequence->addJob(jobB); jobSequence->addJob(jobC); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(jobSequence); Queue::instance()->dequeue(); QVERIFY(Queue::instance()->isEmpty()); } void JobTests::RecursiveQueueAndDequeueAllCollectionTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); JobPointer jobD(new AppendCharacterJob(QChar('d'), &sequence)); JobPointer jobE(new AppendCharacterJob(QChar('e'), &sequence)); JobPointer jobF(new AppendCharacterJob(QChar('f'), &sequence)); JobPointer jobG(new AppendCharacterJob(QChar('g'), &sequence)); JobPointer jobH(new AppendCharacterJob(QChar('h'), &sequence)); JobPointer jobI(new AppendCharacterJob(QChar('i'), &sequence)); JobPointer jobJ(new AppendCharacterJob(QChar('j'), &sequence)); QSharedPointer collection1(new Collection()); collection1->addJob(jobA); collection1->addJob(jobB); collection1->addJob(jobC); QSharedPointer collection2(new Collection()); collection2->addJob(jobD); collection2->addJob(jobE); collection2->addJob(jobF); QSharedPointer collection3(new Collection()); collection3->addJob(jobG); collection3->addJob(jobH); collection3->addJob(jobI); collection3->addJob(jobJ); // sequence 4 will contain sequences 1, 2, and 3, in that order: QSharedPointer collection4(new Collection()); collection4->addJob(collection1); collection4->addJob(collection2); collection4->addJob(collection3); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(collection4); Queue::instance()->dequeue(); QVERIFY(Queue::instance()->isEmpty()); Queue::instance()->resume(); Queue::instance()->finish(); } void JobTests::RecursiveQueueAndDequeueAllSequenceTest() { QString sequence; JobPointer jobA(new AppendCharacterJob(QChar('a'), &sequence)); JobPointer jobB(new AppendCharacterJob(QChar('b'), &sequence)); JobPointer jobC(new AppendCharacterJob(QChar('c'), &sequence)); JobPointer jobD(new AppendCharacterJob(QChar('d'), &sequence)); JobPointer jobE(new AppendCharacterJob(QChar('e'), &sequence)); JobPointer jobF(new AppendCharacterJob(QChar('f'), &sequence)); JobPointer jobG(new AppendCharacterJob(QChar('g'), &sequence)); JobPointer jobH(new AppendCharacterJob(QChar('h'), &sequence)); JobPointer jobI(new AppendCharacterJob(QChar('i'), &sequence)); JobPointer jobJ(new AppendCharacterJob(QChar('j'), &sequence)); QSharedPointer jobSequence1(new Sequence()); jobSequence1->addJob(jobA); jobSequence1->addJob(jobB); jobSequence1->addJob(jobC); QSharedPointer jobSequence2(new Sequence()); jobSequence2->addJob(jobD); jobSequence2->addJob(jobE); jobSequence2->addJob(jobF); QSharedPointer jobSequence3(new Sequence()); jobSequence3->addJob(jobG); jobSequence3->addJob(jobH); jobSequence3->addJob(jobI); jobSequence3->addJob(jobJ); // sequence 4 will contain sequences 1, 2, and 3, in that order: QSharedPointer jobSequence4(new Sequence()); jobSequence4->addJob(jobSequence1); jobSequence4->addJob(jobSequence2); jobSequence4->addJob(jobSequence3); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->suspend(); Queue::instance()->enqueue(jobSequence4); Queue::instance()->dequeue(); QVERIFY(Queue::instance()->isEmpty()); Queue::instance()->resume(); Queue::instance()->finish(); } // This test is not the most efficient, as the mutex locking takes most of // the execution time. Anyway, it will fail if the jobs are not executed // in the right order, and the order is randomized. void JobTests::MassiveJobSequenceTest() { const int NoOfChars = 1024; const char *Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const int SizeOfAlphabet = strlen(Alphabet); AppendCharacterAndVerifyJob jobs[NoOfChars]; Sequence jobSequence; QString sequence; QString in; srand(1); in.reserve(NoOfChars); sequence.reserve(NoOfChars); for (int i = 0; i < NoOfChars; ++i) { const int position = static_cast(SizeOfAlphabet * ((1.0 * rand()) / RAND_MAX)); Q_ASSERT(0 <= position && position < SizeOfAlphabet); QChar c(Alphabet[position]); in.append(c); } for (int i = 0; i < NoOfChars; ++i) { jobs[i].setValues(in.at(i), &sequence, in); jobSequence << jobs[i]; } WaitForIdleAndFinished w(Queue::instance()); QVERIFY(Queue::instance()->isIdle()); enqueue_raw(&jobSequence); Queue::instance()->finish(); QVERIFY(Queue::instance()->isIdle()); QCOMPARE(sequence, in); } void JobTests::SimpleRecursiveSequencesTest() { using namespace ThreadWeaver; QString sequence; Sequence jobSequence1; jobSequence1 << new AppendCharacterJob(QChar('b'), &sequence); Sequence jobSequence2; jobSequence2 << new AppendCharacterJob(QChar('a'), &sequence) << jobSequence1 << new AppendCharacterJob(QChar('c'), &sequence); WaitForIdleAndFinished w(Queue::instance()); stream() << jobSequence2; Queue::instance()->finish(); QCOMPARE(sequence, QString("abc")); } void JobTests::SequenceOfSequencesTest() { QString sequence; AppendCharacterJob jobA(QChar('a'), &sequence); AppendCharacterJob jobB(QChar('b'), &sequence); AppendCharacterJob jobC(QChar('c'), &sequence); AppendCharacterJob jobD(QChar('d'), &sequence); AppendCharacterJob jobE(QChar('e'), &sequence); AppendCharacterJob jobF(QChar('f'), &sequence); AppendCharacterJob jobG(QChar('g'), &sequence); AppendCharacterJob jobH(QChar('h'), &sequence); AppendCharacterJob jobI(QChar('i'), &sequence); AppendCharacterJob jobJ(QChar('j'), &sequence); Sequence jobSequence1; jobSequence1 << jobA << jobB << jobC; Sequence jobSequence2; jobSequence2 << jobD << jobE << jobF; Sequence jobSequence3; jobSequence3 << jobG << jobH << jobI << jobJ; // sequence 4 will contain sequences 1, 2, and 3, in that order: Sequence jobSequence4; jobSequence4 << jobSequence1 << jobSequence2 << jobSequence3; WaitForIdleAndFinished w(Queue::instance()); stream() << jobSequence4; // Job::DumpJobDependencies(); Queue::instance()->finish(); QCOMPARE(sequence, QString("abcdefghij")); } void JobTests::QueueAndStopTest() { QString sequence; AppendCharacterJob a('a', &sequence); AppendCharacterJob b('b', &sequence); AppendCharacterJob c('c', &sequence); FailingAppendCharacterJob d('d', &sequence); AppendCharacterJob e('e', &sequence); AppendCharacterJob f('f', &sequence); AppendCharacterJob g('g', &sequence); Sequence jobSequence; jobSequence << a << b << c << d << e << f << g; WaitForIdleAndFinished w(Queue::instance()); stream() << jobSequence; Queue::instance()->finish(); QCOMPARE(sequence, QString("abcd")); } void JobTests::ResourceRestrictionPolicyBasicsTest() { // this test tests that with resource restrictions assigned, jobs // still get executed as expected QString sequence; ResourceRestrictionPolicy restriction(2); AppendCharacterJob a('a', &sequence); AppendCharacterJob b('b', &sequence); AppendCharacterJob c('c', &sequence); AppendCharacterJob d('d', &sequence); AppendCharacterJob e('e', &sequence); AppendCharacterJob f('f', &sequence); AppendCharacterJob g('g', &sequence); Collection collection; collection << a << b << c << d << e << f << g; Q_FOREACH(AppendCharacterJob* job, QList() << &a << &b << &c << &d << &e << &f << &g) { QMutexLocker l(job->mutex()); job->assignQueuePolicy(&restriction); } WaitForIdleAndFinished w(Queue::instance()); stream() << collection; Queue::instance()->finish(); QVERIFY(Queue::instance()->isIdle()); } void JobTests::jobStarted(JobPointer) { QVERIFY(thread() == QThread::currentThread()); } void JobTests::jobDone(JobPointer) { QVERIFY(thread() == QThread::currentThread()); } void JobTests::JobSignalsAreEmittedAsynchronouslyTest() { using namespace ThreadWeaver; char bits[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; const int NumberOfBits = sizeof bits / sizeof bits[0]; QString sequence; QObjectDecorator collection(new Collection, this); QVERIFY(connect(&collection, SIGNAL(started(ThreadWeaver::JobPointer)), SLOT(jobStarted(ThreadWeaver::JobPointer)))); QVERIFY(connect(&collection, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(jobDone(ThreadWeaver::JobPointer)))); for (int counter = 0; counter < NumberOfBits; ++counter) { QJobPointer job(new QObjectDecorator(new AppendCharacterJob(bits[counter], &sequence))); QVERIFY(connect(job.data(), SIGNAL(started(ThreadWeaver::JobPointer)), SLOT(jobStarted(ThreadWeaver::JobPointer)))); QVERIFY(connect(job.data(), SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(jobDone(ThreadWeaver::JobPointer)))); collection.collection()->addJob(job); } WaitForIdleAndFinished w(Queue::instance()); enqueue_raw(&collection); QCoreApplication::processEvents(); Queue::instance()->finish(); QVERIFY(sequence.length() == NumberOfBits); } QAtomicInt deliveryTestCounter; void JobTests::deliveryTestJobDone(JobPointer) { deliveryTestCounter.fetchAndAddRelease(-1); } void noOp() {} void JobTests::JobSignalsDeliveryTest() { //This test was added to investigate segmentation faults during signal delivery from jobs to the main thread. //Relies on processEvents() processing all pending events, as the specification says. using namespace ThreadWeaver; QCOMPARE(deliveryTestCounter.loadAcquire(), 0); WaitForIdleAndFinished w(Queue::instance()); for (int count = 0; count < 100; ++count) { QJobPointer job(new QObjectDecorator(new Lambda(noOp))); QVERIFY(connect(job.data(), SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(deliveryTestJobDone(ThreadWeaver::JobPointer)))); deliveryTestCounter.fetchAndAddRelease(1); Queue::instance()->enqueue(job); } QCoreApplication::processEvents(); Queue::instance()->finish(); QCoreApplication::processEvents(); QCOMPARE(deliveryTestCounter.loadAcquire(), 0); } void decrementCounter() { deliveryTestCounter.fetchAndAddRelease(-1); } void JobTests::JobPointerExecutionTest() { //This test was added to investigate segmentation faults during signal delivery from jobs to the main thread. //Relies on processEvents() processing all pending events, as the specification says. using namespace ThreadWeaver; QCOMPARE(deliveryTestCounter.loadAcquire(), 0); WaitForIdleAndFinished w(Queue::instance()); for (int count = 0; count < 100; ++count) { JobPointer job(new Lambda(decrementCounter)); deliveryTestCounter.fetchAndAddRelease(1); Queue::instance()->enqueue(job); } QCoreApplication::processEvents(); Queue::instance()->finish(); QCoreApplication::processEvents(); QCOMPARE(deliveryTestCounter.loadAcquire(), 0); } void JobTests::DequeueSuspendedSequenceTest() { using namespace ThreadWeaver; Sequence sequence; Queue weaver; weaver.suspend(); enqueue_raw(&weaver, &sequence); weaver.dequeue(); // don't crash } void JobTests::IdDecoratorDecoratesTest() { using namespace ThreadWeaver; QString sequence; JobPointer job(new IdDecorator(new AppendCharacterJob('a', &sequence))); WaitForIdleAndFinished w(Queue::instance()); Queue::instance()->enqueue(job); Queue::instance()->finish(); QCOMPARE(sequence, QString::fromLatin1("a")); } void JobTests::IdDecoratorAutoDeleteTest() { using namespace ThreadWeaver; - IdDecorator id(0); + IdDecorator id(nullptr); QCOMPARE(id.autoDelete(), true); // autoDelete is on by default id.setAutoDelete(false); QCOMPARE(id.autoDelete(), false); id.setAutoDelete(true); QCOMPARE(id.autoDelete(), true); // now do not crash, even though id decorates a null pointer } void JobTests::IdDecoratorSingleAllocationTest() { using namespace ThreadWeaver; struct DecoratedJob : public IdDecorator { QString sequence; AppendCharacterJob job; DecoratedJob() : IdDecorator(&job, false), job('a', &sequence) {} }; WaitForIdleAndFinished w(Queue::instance()); DecoratedJob job; enqueue_raw(&job); Queue::instance()->finish(); QCOMPARE(job.sequence, QString::fromLatin1("a")); } struct InstanceCountedJob : public Job { static QAtomicInt counter; void run(JobPointer, Thread *) Q_DECL_OVERRIDE { } InstanceCountedJob() { counter.fetchAndAddRelease(1); } ~InstanceCountedJob() { counter.fetchAndAddRelease(-1); } }; QAtomicInt InstanceCountedJob::counter; /** @brief Verify that neither the queue nor the thread keep a reference to the job after completing it. * * This is necessary because user-allocated objects like queue policies may be registered with the jobs. If the jobs stick around * until the thread or queue are deleted, the user-allocatd objects may have gone out of scope or been deleted already, causing * potential errors. From ThreadWeaver's point of view, a job seizes to exist once the processing thread asks for the next job. */ void JobTests::JobsAreDestroyedAfterFinishTest() { using namespace ThreadWeaver; WaitForIdleAndFinished w(Queue::instance()); Q_UNUSED(w); Queue::instance()->suspend(); JobPointer job(new InstanceCountedJob); Queue::instance()->enqueue(job); QCOMPARE(InstanceCountedJob::counter.loadAcquire(), 1); Queue::instance()->resume(); QCOMPARE(InstanceCountedJob::counter.loadAcquire(), 1); Queue::instance()->finish(); QCOMPARE(InstanceCountedJob::counter.loadAcquire(), 1); QCoreApplication::processEvents(); QCOMPARE(InstanceCountedJob::counter.loadAcquire(), 1); job.clear(); // if this succeeds, job is the only shared pointer pointing to the created InstanceCountedJob object: QCOMPARE(InstanceCountedJob::counter.loadAcquire(), 0); } void JobTests::JobExitStatusByExceptionTest() { using namespace ThreadWeaver; struct FailingJob : public Job { void run(JobPointer, Thread *) Q_DECL_OVERRIDE { throw JobFailed(); } }; FailingJob failing; failing.blockingExecute(); QCOMPARE(failing.status(), Job::Status_Failed); struct AbortingJob : public Job { void run(JobPointer, Thread *) Q_DECL_OVERRIDE { throw JobAborted(); } }; AbortingJob aborting; aborting.blockingExecute(); QCOMPARE(aborting.status(), Job::Status_Aborted); struct SuccessfulJob : public Job { void run(JobPointer, Thread *) Q_DECL_OVERRIDE { // do nothing } }; SuccessfulJob successful; successful.blockingExecute(); QCOMPARE(successful.status(), Job::Status_Success); } void JobTests::JobManualExitStatusTest() { using namespace ThreadWeaver; struct FailingJob : public Job { void run(JobPointer, Thread *) Q_DECL_OVERRIDE { setStatus(Job::Status_Failed); } }; FailingJob failing; failing.blockingExecute(); QCOMPARE(failing.status(), Job::Status_Failed); struct AbortingJob : public Job { void run(JobPointer, Thread *) Q_DECL_OVERRIDE { setStatus(Job::Status_Aborted); } }; AbortingJob aborting; aborting.blockingExecute(); QCOMPARE(aborting.status(), Job::Status_Aborted); struct SuccessfulJob : public Job { void run(JobPointer, Thread *) Q_DECL_OVERRIDE { // do nothing } }; SuccessfulJob successful; successful.blockingExecute(); QCOMPARE(successful.status(), Job::Status_Success); } void JobTests::QueueStreamLifecycleTest() { QString sequence; using namespace ThreadWeaver; WaitForIdleAndFinished w(Queue::instance()); Q_UNUSED(w); stream() << make_job(new AppendCharacterJob('a', &sequence)) // enqueues JobPointer << new AppendCharacterJob('b', &sequence) // enqueues JobInterface* << make_job(new AppendCharacterJob('c', &sequence)); Queue::instance()->finish(); QCOMPARE(sequence.count(), 3); } class SynchronizedNumbers { public: void append(int number) { QMutexLocker l(&mutex_); numbers_.append(number); } bool isSorted() const { QMutexLocker l(&mutex_); return std::is_sorted(numbers_.cbegin(), numbers_.cend()); } void sortChunks(int chunkSize) { QMutexLocker l(&mutex_); Q_ASSERT(numbers_.count() % chunkSize == 0); auto start = numbers_.begin(); while(start!=numbers_.end()) { auto stop = start; std::advance(stop, chunkSize); std::sort(start, stop); start = stop; } } private: QVector numbers_; mutable QMutex mutex_; }; class GeneratingEnumeratorSequence : public ThreadWeaver::Sequence { public: GeneratingEnumeratorSequence(SynchronizedNumbers* numbers, int start, int count) : start_(start), count_(count), numbers_(numbers) {} void run(JobPointer, Thread*) Q_DECL_OVERRIDE { numbers_->append(start_); for(int index = start_ + 1; index < start_+count_; ++index) { *this << new GeneratingEnumeratorSequence(numbers_, index, 1); } } private: const int start_; const int count_; SynchronizedNumbers* numbers_; }; class GeneratingEnumeratorCollection : public ThreadWeaver::Collection { public: GeneratingEnumeratorCollection(SynchronizedNumbers* numbers, int start, int count) : start_(start), count_(count), numbers_(numbers) {} void run(JobPointer, Thread*) Q_DECL_OVERRIDE { numbers_->append(start_); QVector elements; for(int index = start_ + 1; index < start_+count_; ++index) { elements.append(new GeneratingEnumeratorCollection(numbers_, index, 1)); } std::random_shuffle(elements.begin(), elements.end()); std::for_each(elements.begin(), elements.end(), [this](QVector::iterator it) { *this << *it; } ); } private: const int start_; const int count_; SynchronizedNumbers* numbers_; }; void JobTests::NestedGeneratingCollectionsTest() { using namespace ThreadWeaver; WaitForIdleAndFinished w(Queue::instance()); Q_UNUSED(w); SynchronizedNumbers numbers; const int NumberOfSequences = 100; const int ElementsPerSequence = 20; Sequence sequence; for(int index = 0; index < NumberOfSequences; ++index) { sequence << new GeneratingEnumeratorCollection(&numbers, index * ElementsPerSequence, ElementsPerSequence); } stream() << sequence; Queue::instance()->finish(); numbers.sortChunks(ElementsPerSequence); QVERIFY(numbers.isSorted()); } void JobTests::NestedGeneratingSequencesTest() { using namespace ThreadWeaver; WaitForIdleAndFinished w(Queue::instance()); Q_UNUSED(w); SynchronizedNumbers numbers; const int NumberOfSequences = 100; const int ElementsPerSequence = 20; Sequence sequence; for(int index = 0; index < NumberOfSequences; ++index) { sequence << new GeneratingEnumeratorSequence(&numbers, index * ElementsPerSequence, ElementsPerSequence); } stream() << sequence; Queue::instance()->finish(); QVERIFY(numbers.isSorted()); } void JobTests::DeeperNestedGeneratingCollectionsTest() { using namespace ThreadWeaver; auto logger = new JobLoggingWeaver(); Queue queue(logger); WaitForIdleAndFinished w(&queue); Q_UNUSED(w); const int ElementsPerCollection = 20; const int NumberOfBlocks = 2; const int CollectionsPerBlock = 2; SynchronizedNumbers numbers; Sequence sequence; for(int block=0; block < NumberOfBlocks; ++block) { auto col = new Collection(); for(int collection = 0; collection < CollectionsPerBlock; ++collection) { const int start = (block * NumberOfBlocks + collection) * ElementsPerCollection; *col << new GeneratingEnumeratorCollection(&numbers, start, ElementsPerCollection); } sequence << col; } queue.stream() << sequence; queue.finish(); numbers.sortChunks(NumberOfBlocks*ElementsPerCollection); QVERIFY(numbers.isSorted()); } QTEST_MAIN(JobTests) diff --git a/autotests/LifecycleTests.cpp b/autotests/LifecycleTests.cpp index 63a32b0..c550d19 100644 --- a/autotests/LifecycleTests.cpp +++ b/autotests/LifecycleTests.cpp @@ -1,115 +1,115 @@ /* -*- C++ -*- This file contains a testsuite for JobPointer behaviour in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include class NotifyOnDeletejob : public ThreadWeaver::Job { public: explicit NotifyOnDeletejob(bool &exists) : ThreadWeaver::Job() , m_exists(exists) { exists = true; } ~NotifyOnDeletejob() { m_exists = false; } void run(ThreadWeaver::JobPointer, ThreadWeaver::Thread *) Q_DECL_OVERRIDE {} private: bool &m_exists; }; class LifecycleTests : public QObject { Q_OBJECT public: LifecycleTests(); private Q_SLOTS: void testJobAutoDeletionBasics(); void testJobAutoDeletion(); }; LifecycleTests::LifecycleTests() { } void LifecycleTests::testJobAutoDeletionBasics() { bool job1Exists = false; bool job2Exists = false; ThreadWeaver::JobPointer job2(new NotifyOnDeletejob(job2Exists)); Q_UNUSED(job2); QCOMPARE(true, job2Exists); { ThreadWeaver::JobPointer job1(new NotifyOnDeletejob(job1Exists)); Q_UNUSED(job1); QCOMPARE(job1Exists, true); } QCOMPARE(job1Exists, false); QCOMPARE(job2Exists, true); } void LifecycleTests::testJobAutoDeletion() { bool job1Exists = false; bool job2Exists = false; { ThreadWeaver::JobPointer job1(new NotifyOnDeletejob(job1Exists)); QCOMPARE(job1Exists, true); int argc = 0; - QCoreApplication app(argc, (char **)0); Q_UNUSED(app); + QCoreApplication app(argc, (char **)nullptr); Q_UNUSED(app); QVERIFY(ThreadWeaver::Queue::instance()); ThreadWeaver::Queue::instance()->suspend(); ThreadWeaver::Queue::instance()->enqueue(job1); ThreadWeaver::Queue::instance()->enqueue(ThreadWeaver::JobPointer(new NotifyOnDeletejob(job2Exists))); QCOMPARE(job2Exists, true); ThreadWeaver::Queue::instance()->resume(); ThreadWeaver::Queue::instance()->finish(); QVERIFY(ThreadWeaver::Queue::instance()->isIdle()); ThreadWeaver::Queue::instance()->suspend(); QCOMPARE(job2Exists, false); QCOMPARE(job1Exists, true); } - QVERIFY(ThreadWeaver::Queue::instance() == 0); + QVERIFY(ThreadWeaver::Queue::instance() == nullptr); QCOMPARE(job2Exists, false); QCOMPARE(job1Exists, false); } QTEST_APPLESS_MAIN(LifecycleTests) #include "LifecycleTests.moc" diff --git a/autotests/QueueFactoryTests.cpp b/autotests/QueueFactoryTests.cpp index 210c6f6..caa02d2 100644 --- a/autotests/QueueFactoryTests.cpp +++ b/autotests/QueueFactoryTests.cpp @@ -1,108 +1,108 @@ /* -*- C++ -*- This file contains a testsuite for global queue customizations in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include using namespace ThreadWeaver; QAtomicInt counter; class CountingJobDecorator : public IdDecorator { public: explicit CountingJobDecorator(const JobPointer &job) : IdDecorator(job.data(), false) , original_(job) {} void run(JobPointer self, Thread *thread) Q_DECL_OVERRIDE { counter.fetchAndAddRelease(1); IdDecorator::run(self, thread); counter.fetchAndAddAcquire(1); } JobPointer original_; }; class JobCountingWeaver : public Weaver { Q_OBJECT public: - explicit JobCountingWeaver(QObject *parent = 0) : Weaver(parent) {} + explicit JobCountingWeaver(QObject *parent = nullptr) : Weaver(parent) {} void enqueue(const QVector &jobs) Q_DECL_OVERRIDE { QVector decorated; std::transform(jobs.begin(), jobs.end(), std::back_inserter(decorated), [](const JobPointer & job) { return JobPointer(new CountingJobDecorator(job)); }); Weaver::enqueue(decorated); } }; class CountingGlobalQueueFactory : public Queue::GlobalQueueFactory { - Queue *create(QObject *parent = 0) Q_DECL_OVERRIDE { + Queue *create(QObject *parent = nullptr) Q_DECL_OVERRIDE { return new Queue(new JobCountingWeaver, parent); } }; int argc = 0; class QueueFactoryTests : public QObject { Q_OBJECT private Q_SLOTS: void testQueueFactory() { counter.storeRelease(0); - QCoreApplication app(argc, (char **)0); + QCoreApplication app(argc, (char **)nullptr); Queue queue(new JobCountingWeaver(this)); queue.enqueue(make_job([]() {})); // nop queue.finish(); QCOMPARE(counter.loadAcquire(), 2); } void testGlobalQueueFactory() { Queue::setGlobalQueueFactory(new CountingGlobalQueueFactory()); - QCoreApplication app(argc, (char **)0); + QCoreApplication app(argc, (char **)nullptr); counter.storeRelease(0); Queue::instance()->enqueue(make_job([]() {})); // nop Queue::instance()->finish(); QCOMPARE(counter.loadAcquire(), 2); } }; QTEST_APPLESS_MAIN(QueueFactoryTests) #include "QueueFactoryTests.moc" diff --git a/autotests/QueueTests.cpp b/autotests/QueueTests.cpp index a758d66..29dbec0 100644 --- a/autotests/QueueTests.cpp +++ b/autotests/QueueTests.cpp @@ -1,206 +1,206 @@ #include "QueueTests.h" #include #include #include #include #include #include "AppendCharacterJob.h" #include /* -*- C++ -*- This file contains a testsuite for the queueing behaviour in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include QMutex s_GlobalMutex; LowPriorityAppendCharacterJob::LowPriorityAppendCharacterJob(QChar c, QString *stringref) : AppendCharacterJob(c, stringref) {} int LowPriorityAppendCharacterJob ::priority() const { return -1; } HighPriorityAppendCharacterJob::HighPriorityAppendCharacterJob(QChar c, QString *stringref) : AppendCharacterJob(c, stringref) {} int HighPriorityAppendCharacterJob::priority() const { return 1; } SecondThreadThatQueues::SecondThreadThatQueues() : QThread() { } void SecondThreadThatQueues::run() { QString sequence; AppendCharacterJob a('a', &sequence); ThreadWeaver::enqueue_raw(&a); ThreadWeaver::Queue::instance()->finish(); QCOMPARE(sequence, QString("a")); } QueueTests::QueueTests(QObject *parent) : QObject(parent) - , autoDeleteJob(0) + , autoDeleteJob(nullptr) { } void QueueTests::initTestCase() { ThreadWeaver::setDebugLevel(true, 1); } void QueueTests::SimpleQueuePrioritiesTest() { using namespace ThreadWeaver; Queue weaver; weaver.setMaximumNumberOfThreads(1); // just one thread QString sequence; LowPriorityAppendCharacterJob jobA(QChar('a'), &sequence); AppendCharacterJob jobB(QChar('b'), &sequence); HighPriorityAppendCharacterJob jobC(QChar('c'), &sequence); // queue low priority, then normal priority, then high priority // if priorities are processed correctly, the jobs will be executed in reverse order weaver.suspend(); enqueue_raw(&weaver, &jobA); enqueue_raw(&weaver, &jobB); enqueue_raw(&weaver, &jobC); weaver.resume(); weaver.finish(); QCOMPARE(sequence, QString("cba")); } void QueueTests::WeaverInitializationTest() { // this one mostly tests the sanity of the startup behaviour ThreadWeaver::Queue weaver; QCOMPARE(weaver.currentNumberOfThreads(), 0); QVERIFY(weaver.isEmpty()); QVERIFY(weaver.isIdle()); QVERIFY(weaver.queueLength() == 0); weaver.finish(); } void QueueTests::QueueFromSecondThreadTest() { ThreadWeaver::Queue::instance(); //create global instance in the main thread SecondThreadThatQueues thread; thread.start(); thread.wait(); QVERIFY(ThreadWeaver::Queue::instance()->isIdle()); } void QueueTests::deleteJob(ThreadWeaver::JobPointer job) { // test that signals are properly emitted (asynchronously, that is): QVERIFY(thread() == QThread::currentThread()); QVERIFY(job == autoDeleteJob); - delete autoDeleteJob; autoDeleteJob = 0; + delete autoDeleteJob; autoDeleteJob = nullptr; } void QueueTests::DeleteDoneJobsFromSequenceTest() { using namespace ThreadWeaver; QString sequence; autoDeleteJob = new QObjectDecorator(new AppendCharacterJob(QChar('a'), &sequence)); AppendCharacterJob b(QChar('b'), &sequence); AppendCharacterJob c(QChar('c'), &sequence); Collection collection; collection << make_job_raw(autoDeleteJob) << b << c; - QVERIFY(autoDeleteJob != 0); + QVERIFY(autoDeleteJob != nullptr); QVERIFY(connect(autoDeleteJob, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(deleteJob(ThreadWeaver::JobPointer)))); stream() << collection; QTest::qWait(100); // return to event queue to make sure signals are delivered Queue::instance()->finish(); QTest::qWait(100); // return to event queue to make sure signals are delivered // no need to delete a, that should be done in deleteJob - QVERIFY(autoDeleteJob == 0); + QVERIFY(autoDeleteJob == nullptr); } void QueueTests::deleteCollection(ThreadWeaver::JobPointer collection) { QVERIFY(thread() == QThread::currentThread()); QVERIFY(collection == autoDeleteCollection); - delete autoDeleteCollection; autoDeleteCollection = 0; + delete autoDeleteCollection; autoDeleteCollection = nullptr; } void QueueTests::DeleteCollectionOnDoneTest() { using namespace ThreadWeaver; QString sequence; autoDeleteCollection = new QObjectDecorator(new Collection); QVERIFY(connect(autoDeleteCollection, SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(deleteCollection(ThreadWeaver::JobPointer)))); AppendCharacterJob a(QChar('a'), &sequence); AppendCharacterJob b(QChar('b'), &sequence); *autoDeleteCollection->collection() << a << b; enqueue_raw(autoDeleteCollection); // return to event queue to make sure signals are delivered // (otherwise, no slot calls would happen before the end of this function) // I assume the amount of time that we wait does not matter QTest::qWait(10); Queue::instance()->finish(); // return to event queue to make sure signals are delivered QTest::qWait(10); // no need to delete a, that should be done in deleteJob QVERIFY(sequence.length() == 2); - QVERIFY(autoDeleteCollection == 0); + QVERIFY(autoDeleteCollection == nullptr); } QTEST_MAIN(QueueTests) diff --git a/autotests/QueueTests.h b/autotests/QueueTests.h index bbda835..5e5e88e 100644 --- a/autotests/QueueTests.h +++ b/autotests/QueueTests.h @@ -1,102 +1,102 @@ /* -*- C++ -*- This file contains a testsuite for the queueing behaviour in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QUEUETESTS_H #define QUEUETESTS_H #include #include "AppendCharacterJob.h" class LowPriorityAppendCharacterJob : public AppendCharacterJob { public: - LowPriorityAppendCharacterJob(QChar character = QChar(), QString *stringref = 0); + LowPriorityAppendCharacterJob(QChar character = QChar(), QString *stringref = nullptr); int priority() const Q_DECL_OVERRIDE; }; class HighPriorityAppendCharacterJob : public AppendCharacterJob { public: - HighPriorityAppendCharacterJob(QChar character = QChar(), QString *stringref = 0); + HighPriorityAppendCharacterJob(QChar character = QChar(), QString *stringref = nullptr); int priority() const Q_DECL_OVERRIDE; }; namespace ThreadWeaver { class Job; class Collection; class QObjectDecorator; } using ThreadWeaver::Job; class SecondThreadThatQueues : public QThread { Q_OBJECT public: SecondThreadThatQueues(); protected: void run() Q_DECL_OVERRIDE; }; class QueueTests : public QObject { Q_OBJECT public: - explicit QueueTests(QObject *parent = 0); + explicit QueueTests(QObject *parent = nullptr); public Q_SLOTS: // this slot (which is not a test) is part of // DeleteDoneJobsFromSequenceTest void deleteJob(ThreadWeaver::JobPointer); // this slot is part of DeleteCollectionOnDoneTest void deleteCollection(ThreadWeaver::JobPointer); private: // this is also part of DeleteDoneJobsFromSequenceTest ThreadWeaver::QObjectDecorator *autoDeleteJob; // this is part of DeleteCollectionOnDoneTest ThreadWeaver::QObjectDecorator *autoDeleteCollection; private Q_SLOTS: void initTestCase(); void SimpleQueuePrioritiesTest(); void WeaverInitializationTest(); void QueueFromSecondThreadTest(); void DeleteDoneJobsFromSequenceTest(); void DeleteCollectionOnDoneTest(); }; #endif diff --git a/autotests/ShutdownOnQApplicationQuitTests.cpp b/autotests/ShutdownOnQApplicationQuitTests.cpp index 832626f..2ae393c 100644 --- a/autotests/ShutdownOnQApplicationQuitTests.cpp +++ b/autotests/ShutdownOnQApplicationQuitTests.cpp @@ -1,65 +1,65 @@ /* -*- C++ -*- This file contains a testsuite for global queue instantiation in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include // The tests checks if the global ThreadWeaver instance is properly // destroyed along with QCoreApplication. After that all the puppies // are sad and the kittens cry, and the test exits. class ShutdownOnQApplicationQuitTests : public QObject { Q_OBJECT public: ShutdownOnQApplicationQuitTests(); private Q_SLOTS: void testShutdownOnQApplicationQuit(); }; ShutdownOnQApplicationQuitTests::ShutdownOnQApplicationQuitTests() { } void ShutdownOnQApplicationQuitTests::testShutdownOnQApplicationQuit() { { int argc = 0; - QCoreApplication app(argc, (char **)0); - QVERIFY(ThreadWeaver::Queue::instance() != 0); + QCoreApplication app(argc, (char **)nullptr); + QVERIFY(ThreadWeaver::Queue::instance() != nullptr); ThreadWeaver::Queue::instance()->suspend(); ThreadWeaver::Queue::instance()->resume(); QTest::qWait(10); } - QVERIFY(ThreadWeaver::Queue::instance() == 0); + QVERIFY(ThreadWeaver::Queue::instance() == nullptr); } QTEST_APPLESS_MAIN(ShutdownOnQApplicationQuitTests) #include "ShutdownOnQApplicationQuitTests.moc" diff --git a/examples/HelloInternet/MainWidget.h b/examples/HelloInternet/MainWidget.h index 77eb679..2958b3c 100644 --- a/examples/HelloInternet/MainWidget.h +++ b/examples/HelloInternet/MainWidget.h @@ -1,30 +1,30 @@ #ifndef MAINWIDGET_H #define MAINWIDGET_H #include #include #include class MainWidget : public QWidget { Q_OBJECT public: - explicit MainWidget(QWidget *parent = 0); + explicit MainWidget(QWidget *parent = nullptr); ~MainWidget(); protected: void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; public Q_SLOTS: void setImage(QImage image); void setCaption(QString text); void setStatus(QString text); private: QLabel* m_image; QLabel* m_caption; QLabel* m_status; }; #endif // MAINWIDGET_H diff --git a/examples/HelloWorldRaw/HelloWorldRaw.cpp b/examples/HelloWorldRaw/HelloWorldRaw.cpp index 160632f..14c5227 100644 --- a/examples/HelloWorldRaw/HelloWorldRaw.cpp +++ b/examples/HelloWorldRaw/HelloWorldRaw.cpp @@ -1,62 +1,62 @@ /* -*- C++ -*- This file is part of ThreadWeaver. Author: Mirko Boehm Copyright: (C) 2005-2014 Mirko Boehm Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include using namespace ThreadWeaver; //@@snippet_begin(sample-helloworldraw-class) class QDebugJob : public Job { public: - QDebugJob(const char* message = 0) : m_message(message) {} + QDebugJob(const char* message = nullptr) : m_message(message) {} protected: void run(JobPointer, Thread*) Q_DECL_OVERRIDE { qDebug() << m_message; } private: const char* m_message; }; //@@snippet_end //@@snippet_begin(sample-helloworldraw-main) int main(int argc, char** argv) { QCoreApplication app(argc, argv); // Allocate jobs as local variables: QDebugJob j1("Hello"); QDebugJob j2("World!"); JobPointer j3(new QDebugJob("This is...")); Job* j4 = new QDebugJob("ThreadWeaver!"); // Queue the Job using the default Queue stream: stream() << j1 << j2 // local variables << j3 // a shared pointer << j4; // a raw pointer // Wait for finish(), because job is destroyed // before the global queue: Queue::instance()->finish(); } //@@snippet_end diff --git a/examples/ThumbNailer/AverageLoadManager.h b/examples/ThumbNailer/AverageLoadManager.h index 43d6eb1..8fed6fc 100644 --- a/examples/ThumbNailer/AverageLoadManager.h +++ b/examples/ThumbNailer/AverageLoadManager.h @@ -1,29 +1,29 @@ #ifndef AVERAGELOADMANAGER_H #define AVERAGELOADMANAGER_H #include class QTimer; class AverageLoadManager : public QObject { Q_OBJECT public: - explicit AverageLoadManager(QObject *parent = 0); + explicit AverageLoadManager(QObject *parent = nullptr); void activate(bool enabled); bool available() const; QPair workersRange() const; Q_SIGNALS: void recommendedWorkerCount(int); private Q_SLOTS: void update(); private: QTimer* m_timer; int m_min, m_max; }; #endif // AVERAGELOADMANAGER_H diff --git a/examples/ThumbNailer/Image.h b/examples/ThumbNailer/Image.h index 0846117..a9635e4 100644 --- a/examples/ThumbNailer/Image.h +++ b/examples/ThumbNailer/Image.h @@ -1,100 +1,100 @@ /* -*- C++ -*- This file is part of ThreadWeaver. Author: Mirko Boehm Copyright: (C) 2005-2014 Mirko Boehm Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IMAGE_H #define IMAGE_H #include #include #include #include #include "Progress.h" class Model; /** @brief Image loads an image from a path, and then calculates and saves a thumbnail for it. */ class Image { Q_DECLARE_TR_FUNCTIONS(Image) public: enum Steps { Step_NotStarted, Step_LoadFile, Step_LoadImage, Step_ComputeThumbNail, Step_SaveThumbNail, Step_NumberOfSteps = Step_SaveThumbNail, Step_Complete = Step_SaveThumbNail }; Image(const QString inputFileName = QString(), const QString outputFileName = QString(), - Model* model = 0, int id = 0); + Model* model = nullptr, int id = 0); Progress progress() const; QString description() const; QString details() const; QString details2() const; int processingOrder() const; const QString inputFileName() const; const QString outputFileName() const; QImage thumbNail() const; void loadFile(); void loadImage(); void computeThumbNail(); void saveThumbNail(); static const int ThumbHeight; static const int ThumbWidth; private: void announceProgress(Steps step); void error(Steps step, const QString& message); QString m_inputFileName; QString m_outputFileName; QString m_description; QString m_details; QString m_details2; QAtomicInt m_progress; QAtomicInt m_failedStep; QAtomicInt m_processingOrder; QByteArray m_imageData; QImage m_image; QImage m_thumbnail; Model* m_model; int m_id; static QReadWriteLock Lock; static int ProcessingOrder; }; Q_DECLARE_METATYPE(Image) Q_DECLARE_METATYPE(const Image*) #endif // IMAGE_H diff --git a/examples/ThumbNailer/ImageListFilter.h b/examples/ThumbNailer/ImageListFilter.h index c65e30c..713584b 100644 --- a/examples/ThumbNailer/ImageListFilter.h +++ b/examples/ThumbNailer/ImageListFilter.h @@ -1,41 +1,41 @@ /* -*- C++ -*- This file is part of ThreadWeaver. Author: Mirko Boehm Copyright: (C) 2005-2014 Mirko Boehm Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef IMAGELISTFILTER_H #define IMAGELISTFILTER_H #include #include "Image.h" class ImageListFilter : public QSortFilterProxyModel { Q_OBJECT public: - explicit ImageListFilter(Image::Steps step, QObject* parent = 0); + explicit ImageListFilter(Image::Steps step, QObject* parent = nullptr); bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const Q_DECL_OVERRIDE; private: Image::Steps m_step; }; #endif // IMAGELISTFILTER_H diff --git a/examples/ThumbNailer/ItemDelegate.h b/examples/ThumbNailer/ItemDelegate.h index ebf888f..d419aed 100644 --- a/examples/ThumbNailer/ItemDelegate.h +++ b/examples/ThumbNailer/ItemDelegate.h @@ -1,36 +1,36 @@ /* -*- C++ -*- This file declares the SMIVItemDelegate class. $ Author: Mirko Boehm $ $ Copyright: (C) 2005, Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://www.hackerbuero.org $ $ License: LGPL with the following explicit clarification: This code may be linked against any version of the Qt toolkit from Trolltech, Norway. $ $Id: SMIVItemDelegate.h 30 2005-08-16 16:16:04Z mirko $ */ #ifndef ITEMDELEGATE_H #define ITEMDELEGATE_H #include #include class ItemDelegate : public QItemDelegate { Q_OBJECT public: - ItemDelegate(QObject* parent = 0); + ItemDelegate(QObject* parent = nullptr); static const int FrameWidth; static const int TextMargin; static const int Margin; private: QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex& index) const Q_DECL_OVERRIDE; void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const Q_DECL_OVERRIDE; }; #endif // SMIVITEMDELEGATE diff --git a/examples/ThumbNailer/MainWindow.h b/examples/ThumbNailer/MainWindow.h index 427363d..cf1bd28 100644 --- a/examples/ThumbNailer/MainWindow.h +++ b/examples/ThumbNailer/MainWindow.h @@ -1,84 +1,84 @@ /* -*- C++ -*- This file is part of ThreadWeaver. Author: Mirko Boehm Copyright: (C) 2005-2014 Mirko Boehm Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "Model.h" class ImageListFilter; namespace Ui { class MainWindow; } class AverageLoadManager; class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow(QWidget *parent = 0); + explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); protected: void closeEvent(QCloseEvent*) Q_DECL_OVERRIDE; public Q_SLOTS: void slotProgress(int step, int total); private Q_SLOTS: void slotOpenFiles(); void slotSelectOutputDirectory(); void slotFileLoaderCapChanged(); void slotImageLoaderCapChanged(); void slotComputeThumbNailCapChanged(); void slotSaveThumbNailCapChanged(); void slotWorkerCapChanged(); void slotEnableAverageLoadManager(bool); void slotRecommendedWorkerCountChanged(int); void slotQuit(); private: Ui::MainWindow *ui; QString m_outputDirectory; Model m_model; ImageListFilter* m_fileLoaderFilter; ImageListFilter* m_imageLoaderFilter; ImageListFilter* m_imageScalerFilter; ImageListFilter* m_imageWriterFilter; AverageLoadManager* m_averageLoadManager; static const QString Setting_OpenLocation; static const QString Setting_OutputLocation; static const QString Setting_WindowState; static const QString Setting_WindowGeometry; }; #endif // MAINWINDOW_H diff --git a/examples/ThumbNailer/Model.h b/examples/ThumbNailer/Model.h index ffd951e..6e12a86 100644 --- a/examples/ThumbNailer/Model.h +++ b/examples/ThumbNailer/Model.h @@ -1,93 +1,93 @@ /* -*- C++ -*- This file is part of ThreadWeaver. Author: Mirko Boehm Copyright: (C) 2005-2014 Mirko Boehm Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MODEL_H #define MODEL_H #include #include #include #include #include "Progress.h" #include "Image.h" class Model : public QAbstractListModel { Q_OBJECT public: enum Roles { Role_SortRole = Qt::UserRole, Role_ImageRole, Role_StepRole }; - explicit Model(QObject *parent = 0); + explicit Model(QObject *parent = nullptr); int fileLoaderCap() const; void setFileLoaderCap(int cap); int imageLoaderCap() const; void setImageLoaderCap(int cap); int computeThumbNailCap() const; void setComputeThumbNailCap(int cap); int saveThumbNailCap() const; void setSaveThumbNailCap(int cap); void clear(); void prepareConversions(const QFileInfoList& filenames, const QString& outputDirectory); bool computeThumbNailsBlockingInLoop(); bool computeThumbNailsBlockingConcurrent(); void queueUpConversion(const QStringList& files, const QString& outputDirectory); Progress progress() const; void progressChanged(); void elementChanged(int id); int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; Q_SIGNALS: void completed(); void progress(int, int); void signalElementChanged(int); private Q_SLOTS: void slotElementChanged(int id); private: QVector m_images; ThreadWeaver::ResourceRestrictionPolicy m_fileLoaderRestriction; ThreadWeaver::ResourceRestrictionPolicy m_imageLoaderRestriction; ThreadWeaver::ResourceRestrictionPolicy m_imageScalerRestriction; ThreadWeaver::ResourceRestrictionPolicy m_fileWriterRestriction; }; #endif // MODEL_H diff --git a/src/collection.cpp b/src/collection.cpp index 882b2ba..9bb75c0 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -1,213 +1,213 @@ /* -*- C++ -*- This file implements the Collection class. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "collection.h" #include "queueapi.h" #include "debuggingaids.h" #include "queueing.h" #include "collection_p.h" #include #include #include #include "dependencypolicy.h" #include "executewrapper_p.h" #include "thread.h" namespace ThreadWeaver { class CollectionExecuteWrapper : public ExecuteWrapper { public: CollectionExecuteWrapper() - : collection(0) + : collection(nullptr) {} void setCollection(Collection *collection_) { collection = collection_; } void begin(const JobPointer& job, Thread *thread) Q_DECL_OVERRIDE { TWDEBUG(4, "CollectionExecuteWrapper::begin: collection %p\n", collection); ExecuteWrapper::begin(job, thread); Q_ASSERT(collection); collection->d()->elementStarted(collection, job, thread); ExecuteWrapper::begin(job, thread); } void end(const JobPointer& job, Thread *thread) Q_DECL_OVERRIDE { TWDEBUG(4, "CollectionExecuteWrapper::end: collection %p\n", collection); Q_ASSERT(collection); ExecuteWrapper::end(job, thread); collection->d()->elementFinished(collection, job, thread); } void cleanup(const JobPointer& job, Thread *) Q_DECL_OVERRIDE { //Once job is unwrapped from us, this object is dangling. Job::executor points to the next higher up execute wrapper. //It is thus safe to "delete this". By no means add any later steps after delete! delete unwrap(job); } private: ThreadWeaver::Collection *collection; }; Collection::Collection() : Job(new Private::Collection_Private) { } Collection::Collection(Private::Collection_Private *d__) : Job(d__) { } Collection::~Collection() { MUTEX_ASSERT_UNLOCKED(mutex()); // dequeue all remaining jobs: QMutexLocker l(mutex()); Q_UNUSED(l); - if (d()->api != 0) { // still queued + if (d()->api != nullptr) { // still queued d()->dequeueElements(this, false); } } void Collection::addJob(JobPointer job) { QMutexLocker l(mutex()); Q_UNUSED(l); - REQUIRE(d()->api == 0 || d()->selfIsExecuting == true); // not queued yet or still running - REQUIRE(job != 0); + REQUIRE(d()->api == nullptr || d()->selfIsExecuting == true); // not queued yet or still running + REQUIRE(job != nullptr); CollectionExecuteWrapper *wrapper = new CollectionExecuteWrapper(); wrapper->setCollection(this); wrapper->wrap(job->setExecutor(wrapper)); d()->elements.append(job); } void Collection::stop(JobPointer job) { Q_UNUSED(job); QMutexLocker l(mutex()); Q_UNUSED(l); d()->stop_locked(this); } void Collection::aboutToBeQueued_locked(QueueAPI *api) { Q_ASSERT(!mutex()->tryLock()); - Q_ASSERT(d()->api == 0); // never queue twice + Q_ASSERT(d()->api == nullptr); // never queue twice d()->api = api; d()->selfExecuteWrapper.wrap(setExecutor(&d()->selfExecuteWrapper)); CollectionExecuteWrapper *wrapper = new CollectionExecuteWrapper(); wrapper->setCollection(this); wrapper->wrap(setExecutor(wrapper)); Job::aboutToBeQueued_locked(api); } void Collection::aboutToBeDequeued_locked(QueueAPI *api) { Q_ASSERT(!mutex()->tryLock()); Q_ASSERT(api && d()->api == api); d()->dequeueElements(this, true); - d()->api = 0; + d()->api = nullptr; Job::aboutToBeDequeued_locked(api); } void Collection::execute(const JobPointer& job, Thread *thread) { { QMutexLocker l(mutex()); Q_UNUSED(l); Q_ASSERT(d()->self.isNull()); - Q_ASSERT(d()->api != 0); + Q_ASSERT(d()->api != nullptr); d()->self = job; d()->selfIsExecuting = true; // reset in elementFinished } Job::execute(job, thread); } void Collection::run(JobPointer, Thread *) { //empty } Private::Collection_Private *Collection::d() { return reinterpret_cast(Job::d()); } const Private::Collection_Private *Collection::d() const { return reinterpret_cast(Job::d()); } JobPointer Collection::jobAt(int i) { Q_ASSERT(!mutex()->tryLock()); Q_ASSERT(i >= 0 && i < d()->elements.size()); return d()->elements.at(i); } int Collection::elementCount() const { QMutexLocker l(mutex()); Q_UNUSED(l); return jobListLength_locked(); } int Collection::jobListLength() const { QMutexLocker l(mutex()); Q_UNUSED(l); return jobListLength_locked(); } int Collection::jobListLength_locked() const { return d()->elements.size(); } Collection &Collection::operator<<(JobInterface *job) { addJob(make_job(job)); return *this; } Collection &Collection::operator<<(const JobPointer &job) { addJob(job); return *this; } Collection &Collection::operator<<(JobInterface &job) { addJob(make_job_raw(&job)); return *this; } } diff --git a/src/collection_p.cpp b/src/collection_p.cpp index aa13b70..8b43dd6 100644 --- a/src/collection_p.cpp +++ b/src/collection_p.cpp @@ -1,199 +1,199 @@ /* -*- C++ -*- This file is part of ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "managedjobpointer.h" #include "debuggingaids.h" #include "queueapi.h" #include "collection_p.h" #include "collection.h" namespace ThreadWeaver { namespace Private { Collection_Private::Collection_Private() - : api(0) + : api(nullptr) , jobCounter(0) , selfIsExecuting(false) { } Collection_Private::~Collection_Private() { } void Collection_Private::finalCleanup(Collection *collection) { Q_ASSERT(!self.isNull()); Q_ASSERT(!mutex.tryLock()); freeQueuePolicyResources(self); if (collection->status() < Job::Status_Success) { collection->setStatus(Job::Status_Success); } - api = 0; + api = nullptr; } void Collection_Private::enqueueElements() { Q_ASSERT(!mutex.tryLock()); prepareToEnqueueElements(); jobCounter.fetchAndStoreOrdered(elements.count() + 1); //including self api->enqueue(elements); } void Collection_Private::elementStarted(Collection*, JobPointer job, Thread*) { QMutexLocker l(&mutex); Q_UNUSED(l); Q_UNUSED(job) // except in Q_ASSERT Q_ASSERT(!self.isNull()); if (jobsStarted.fetchAndAddOrdered(1) == 0) { //emit started() signal on beginning of first job execution selfExecuteWrapper.callBegin(); } } namespace { struct MutexUnlocker { QMutexLocker* locker; MutexUnlocker(QMutexLocker* l) : locker(l) { locker->unlock(); } ~MutexUnlocker() { locker->relock(); } }; } void Collection_Private::elementFinished(Collection *collection, JobPointer job, Thread *thread) { JobPointer saveYourSelf = self; Q_UNUSED(saveYourSelf); QMutexLocker l(&mutex); Q_UNUSED(l); Q_ASSERT(!self.isNull()); Q_UNUSED(job) // except in Q_ASSERT if (selfIsExecuting) { // the element that is finished is the collection itself // the collection is always executed first // queue the collection elements: enqueueElements(); selfIsExecuting = false; } const int started = jobsStarted.loadAcquire(); Q_ASSERT(started >= 0); Q_UNUSED(started); processCompletedElement(collection, job, thread); const int remainingJobs = jobCounter.fetchAndAddOrdered(-1) - 1; TWDEBUG(4, "Collection_Private::elementFinished: %i\n", remainingJobs); if (remainingJobs <= -1) { //its no use to count, the elements have been dequeued, now the threads call back that have been processing jobs in the meantime } else { Q_ASSERT(remainingJobs >= 0); if (remainingJobs == 0) { // all elements can only be done if self has been executed: // there is a small chance that (this) has been dequeued in the // meantime, in this case, there is nothing left to clean up finalCleanup(collection); { MutexUnlocker u(&l); Q_UNUSED(u); selfExecuteWrapper.callEnd(); } self.clear(); } } } void Collection_Private::prepareToEnqueueElements() { //empty in Collection } void Collection_Private::processCompletedElement(Collection*, JobPointer, Thread*) { //empty in Collection } void Collection_Private::stop_locked(Collection *collection) { Q_ASSERT(!mutex.tryLock()); - if (api != 0) { + if (api != nullptr) { TWDEBUG(4, "Collection::stop: dequeueing %p.\n", collection); if (!api->dequeue(ManagedJobPointer(collection))) { dequeueElements(collection, false); } } } void Collection_Private::dequeueElements(Collection* collection, bool queueApiIsLocked) { // dequeue everything: Q_ASSERT(!mutex.tryLock()); - if (api == 0) { + if (api == nullptr) { return; //not queued } for (int index = 0; index < elements.size(); ++index) { bool result; if (queueApiIsLocked) { result = api->dequeue_p(elements.at(index)); } else { result = api->dequeue(elements.at(index)); } if (result) { jobCounter.fetchAndAddOrdered(-1); } TWDEBUG(3, "Collection::Private::dequeueElements: dequeueing %p (%s, %i jobs left).\n", (void *)elements.at(index).data(), result ? "found" : "not found", jobCounter.loadAcquire()); elementDequeued(elements.at(index)); } if (jobCounter.loadAcquire() == 1) { finalCleanup(collection); } } void CollectionSelfExecuteWrapper::begin(const JobPointer &job, Thread *thread) { job_ = job; thread_ = thread; } void CollectionSelfExecuteWrapper::end(const JobPointer &job, Thread *thread) { Q_ASSERT(job_ == job && thread_ == thread); Q_UNUSED(job); Q_UNUSED(thread); //except in assert } void CollectionSelfExecuteWrapper::callBegin() { ExecuteWrapper::begin(job_, thread_); } void CollectionSelfExecuteWrapper::callEnd() { ExecuteWrapper::end(job_, thread_); job_.clear(); } } } diff --git a/src/dependencypolicy.cpp b/src/dependencypolicy.cpp index 4b67eef..79b69a3 100644 --- a/src/dependencypolicy.cpp +++ b/src/dependencypolicy.cpp @@ -1,218 +1,218 @@ /* -*- C++ -*- This file implements the DependencyPolicy class. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id: DebuggingAids.cpp 20 2005-08-08 21:02:51Z mirko $ */ #include "dependencypolicy.h" #include #include #include #include "job.h" #include "debuggingaids.h" #include "managedjobpointer.h" #include "dependency.h" using namespace ThreadWeaver; typedef QMultiMap JobMultiMap; class DependencyPolicy::Private { public: /** A container to keep track of Job dependencies. * For each dependency A->B, which means Job B depends on Job A and may only be executed after A has been * finished, an entry will be added with key A and value B. When A is finished, the entry will be removed. */ JobMultiMap &dependencies() { return depMap_; } QMutex *mutex() { return &mutex_; } JobMultiMap depMap_; QMutex mutex_; }; DependencyPolicy::DependencyPolicy() : QueuePolicy() , d(new Private()) { } DependencyPolicy::~DependencyPolicy() { delete d; } void DependencyPolicy::addDependency(JobPointer jobA, JobPointer jobB) { // jobA depends on jobB - REQUIRE(jobA != 0 && jobB != 0 && jobA != jobB); + REQUIRE(jobA != nullptr && jobB != nullptr && jobA != jobB); QMutexLocker a(jobA->mutex()); QMutexLocker b(jobB->mutex()); QMutexLocker l(d->mutex()); jobA->assignQueuePolicy(this); jobB->assignQueuePolicy(this); d->dependencies().insert(jobA, jobB); TWDEBUG(2, "inserted dependency %p->%p.\n", jobA.data(), jobB.data()); ENSURE(d->dependencies().contains(jobA)); } void DependencyPolicy::addDependency(const Dependency &dep) { addDependency(dep.dependent(), dep.dependee()); } bool DependencyPolicy::removeDependency(JobPointer jobA, JobPointer jobB) { - REQUIRE(jobA != 0 && jobB != 0); + REQUIRE(jobA != nullptr && jobB != nullptr); bool result = false; QMutexLocker l(d->mutex()); // there may be only one (!) occurrence of [this, dep]: QMutableMapIterator it(d->dependencies()); while (it.hasNext()) { it.next(); if (it.key() == jobA && it.value() == jobB) { it.remove(); TWDEBUG(2, "removed dependency %p->%p.\n", jobA.data(), jobB.data()); result = true; break; } } TWDEBUG(result == false, 2, "cannot remove dependency %p->%p, not found.\n", jobA.data(), jobB.data()); ENSURE(! d->dependencies().keys(jobB).contains(jobA)); return result; } bool DependencyPolicy::removeDependency(const Dependency &dep) { return removeDependency(dep.dependent(), dep.dependee()); } void DependencyPolicy::resolveDependencies(JobPointer job) { if (job->success()) { QMutexLocker l(d->mutex()); QMutableMapIterator it(d->dependencies()); // there has to be a better way to do this: (?) while (it.hasNext()) { // we remove all entries where jobs depend on *this* : it.next(); if (it.value() == job) { TWDEBUG(2, "resolved dependencies for %p: %p->%p.\n", job.data(), it.key().data(), it.value().data()); it.remove(); } } } } //QList DependencyPolicy::getDependencies(JobPointer job) const //{ // REQUIRE (job != 0); // QList result; // JobMultiMap::const_iterator it; // QMutexLocker l( & d->mutex() ); // for ( it = d->dependencies().constBegin(); it != d->dependencies().constEnd(); ++it ) // { // if ( it.key() == job ) // { // result.append( it.value() ); // } // } // return result; //} bool DependencyPolicy::hasUnresolvedDependencies(JobPointer job) const { - REQUIRE(job != 0); + REQUIRE(job != nullptr); QMutexLocker l(d->mutex()); return d->dependencies().contains(job); } bool DependencyPolicy::isEmpty() const { QMutexLocker l(d->mutex()); return d->dependencies().isEmpty(); } DependencyPolicy &DependencyPolicy::instance() { static DependencyPolicy policy; return policy; } bool DependencyPolicy::canRun(JobPointer job) { - REQUIRE(job != 0); + REQUIRE(job != nullptr); return !hasUnresolvedDependencies(job); } void DependencyPolicy::free(JobPointer job) { - REQUIRE(job != 0); + REQUIRE(job != nullptr); REQUIRE(job->status() > Job::Status_Running); if (job->success()) { resolveDependencies(job); TWDEBUG(3, "DependencyPolicy::free: dependencies resolved for job %p.\n", (void *)job.data()); } else { TWDEBUG(3, "DependencyPolicy::free: not resolving dependencies for %p (execution not successful).\n", (void *)job.data()); } ENSURE((!hasUnresolvedDependencies(job) && job->success()) || !job->success()); } void DependencyPolicy::release(JobPointer job) { - REQUIRE(job != 0); Q_UNUSED(job) + REQUIRE(job != nullptr); Q_UNUSED(job) } void DependencyPolicy::destructed(JobInterface *job) { - REQUIRE(job != 0); + REQUIRE(job != nullptr); resolveDependencies(ManagedJobPointer(job)); } //void DependencyPolicy::dumpJobDependencies() //{ // QMutexLocker l( & d->mutex() ); // debug ( 0, "Job Dependencies (left depends on right side):\n" ); // for ( JobMultiMap::const_iterator it = d->dependencies().constBegin(); it != d->dependencies().constEnd(); ++it ) // { // debug( 0, " : %p <-- %p\n", (void*)it.key(), (void*)it.value()); // } // debug ( 0, "-----------------\n" ); //} diff --git a/src/destructedstate.cpp b/src/destructedstate.cpp index a03dcf1..16959f0 100644 --- a/src/destructedstate.cpp +++ b/src/destructedstate.cpp @@ -1,127 +1,127 @@ /* -*- C++ -*- This file implements the DestructedState class. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id: DestructedState.cpp 30 2005-08-16 16:16:04Z mirko $ */ #include "destructedstate.h" namespace ThreadWeaver { DestructedState::DestructedState(QueueSignals *) - : WeaverImplState(0) // make sure we cannot use weaver, ever :-) + : WeaverImplState(nullptr) // make sure we cannot use weaver, ever :-) { } void DestructedState::shutDown() { } Weaver *DestructedState::weaver() { - return 0; + return nullptr; } const Weaver *DestructedState::weaver() const { - return 0; + return nullptr; } void DestructedState::setMaximumNumberOfThreads(int) { } int DestructedState::maximumNumberOfThreads() const { return 0; } int DestructedState::currentNumberOfThreads() const { return 0; } void DestructedState::enqueue(const QVector &) { } bool DestructedState::dequeue(const JobPointer &) { return false; } void DestructedState::dequeue() { } void DestructedState::finish() { } bool DestructedState::isEmpty() const { return true; } bool DestructedState::isIdle() const { return true; } int DestructedState::queueLength() const { return 0; } void DestructedState::requestAbort() { } void DestructedState::suspend() { } void DestructedState::resume() { } JobPointer DestructedState::applyForWork(Thread *, bool wasBusy) { Q_UNUSED(wasBusy) // except in Q_ASSERT Q_ASSERT(wasBusy == false); return JobPointer(); } void DestructedState::waitForAvailableJob(Thread *) { } StateId DestructedState::stateId() const { return Destructed; } } diff --git a/src/executewrapper.cpp b/src/executewrapper.cpp index 27de888..e92002f 100644 --- a/src/executewrapper.cpp +++ b/src/executewrapper.cpp @@ -1,75 +1,75 @@ /* -*- C++ -*- Class to manipulate job execution in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "executewrapper_p.h" namespace ThreadWeaver { ExecuteWrapper::ExecuteWrapper() { } Executor *ExecuteWrapper::wrap(Executor *previous) { return wrapped.fetchAndStoreOrdered(previous); } Executor *ExecuteWrapper::unwrap(const JobPointer &job) { Executor *executor = job->setExecutor(wrapped.fetchAndAddOrdered(0)); Q_ASSERT_X(executor == this, Q_FUNC_INFO, "ExecuteWrapper can only unwrap itself!"); - wrapped.fetchAndStoreOrdered(0); + wrapped.fetchAndStoreOrdered(nullptr); return executor; } void ExecuteWrapper::begin(const JobPointer& job, Thread *thread) { - Q_ASSERT(wrapped.loadAcquire() != 0); + Q_ASSERT(wrapped.loadAcquire() != nullptr); wrapped.loadAcquire()->begin(job, thread); } void ExecuteWrapper::execute(const JobPointer& job, Thread *thread) { executeWrapped(job, thread); } void ExecuteWrapper::executeWrapped(const JobPointer& job, Thread *thread) { Executor *executor = wrapped.loadAcquire(); - Q_ASSERT_X(executor != 0, Q_FUNC_INFO, "Wrapped Executor cannot be zero!"); + Q_ASSERT_X(executor != nullptr, Q_FUNC_INFO, "Wrapped Executor cannot be zero!"); executor->execute(job, thread); } void ExecuteWrapper::end(const JobPointer& job, Thread *thread) { - Q_ASSERT(wrapped.loadAcquire() != 0); + Q_ASSERT(wrapped.loadAcquire() != nullptr); wrapped.loadAcquire()->end(job, thread); } } #include "executewrapper_p.h" diff --git a/src/iddecorator.cpp b/src/iddecorator.cpp index 3c4c2fb..94b4eb5 100644 --- a/src/iddecorator.cpp +++ b/src/iddecorator.cpp @@ -1,231 +1,231 @@ /* -*- C++ -*- Base class for job decorators in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "collection.h" #include "sequence.h" #include "managedjobpointer.h" #include "iddecorator.h" namespace { const quintptr IdDecorator_AutoDelete = 1; } namespace ThreadWeaver { // Pssst: IdDecorator uses the d pointer to hold decoratee. It also uses d2 as a bitfield to store the // autoDelete setting. The goal is not to require a dynamic allocation on creation. IdDecorator::IdDecorator(JobInterface *decoratee, bool autoDelete) : d1(reinterpret_cast(decoratee)) - , d2(0) + , d2(nullptr) { setAutoDelete(autoDelete); } IdDecorator::~IdDecorator() { // Do not assert here. IdDecorator can decorate a null pointer. Only assert if a method is called on a decorared // null pointer. if (autoDelete()) { delete job(); } } QMutex *IdDecorator::mutex() const { Q_ASSERT(d1); return job()->mutex(); } void IdDecorator::run(JobPointer self, Thread *thread) { Q_ASSERT(d1); job()->run(self, thread); } void IdDecorator::defaultBegin(const JobPointer& self, Thread *thread) { Q_ASSERT(d1); job()->defaultBegin(self, thread); } void IdDecorator::defaultEnd(const JobPointer& self, Thread *thread) { Q_ASSERT(d1); job()->defaultEnd(self, thread); } void IdDecorator::removeQueuePolicy(QueuePolicy *policy) { Q_ASSERT(d1); job()->removeQueuePolicy(policy); } QList IdDecorator::queuePolicies() const { Q_ASSERT(d1); return job()->queuePolicies(); } void IdDecorator::assignQueuePolicy(QueuePolicy *policy) { Q_ASSERT(d1); job()->assignQueuePolicy(policy); } bool IdDecorator::isFinished() const { Q_ASSERT(d1); return job()->isFinished(); } void IdDecorator::aboutToBeQueued(QueueAPI *api) { Q_ASSERT(d1); job()->aboutToBeQueued(api); } void IdDecorator::aboutToBeQueued_locked(QueueAPI *api) { Q_ASSERT(d1); job()->aboutToBeQueued_locked(api); } void IdDecorator::aboutToBeDequeued(QueueAPI *api) { Q_ASSERT(d1); job()->aboutToBeDequeued(api); } void IdDecorator::aboutToBeDequeued_locked(QueueAPI *api) { Q_ASSERT(d1); job()->aboutToBeDequeued_locked(api); } void IdDecorator::requestAbort() { Q_ASSERT(d1); job()->requestAbort(); } bool IdDecorator::success() const { Q_ASSERT(d1); return job()->success(); } int IdDecorator::priority() const { Q_ASSERT(d1); return job()->priority(); } void IdDecorator::setStatus(JobInterface::Status status) { Q_ASSERT(d1); job()->setStatus(status); } JobInterface::Status IdDecorator::status() const { Q_ASSERT(d1); return job()->status(); } Executor *IdDecorator::executor() const { Q_ASSERT(d1); return job()->executor(); } Executor *IdDecorator::setExecutor(Executor *executor) { Q_ASSERT(d1); return job()->setExecutor(executor); } void IdDecorator::execute(const JobPointer& self, ThreadWeaver::Thread *thread) { Q_ASSERT(d1); job()->execute(self, thread); } void IdDecorator::blockingExecute() { Q_ASSERT(d1); job()->blockingExecute(); } const ThreadWeaver::JobInterface *IdDecorator::job() const { return reinterpret_cast(d1); } JobInterface *IdDecorator::job() { return reinterpret_cast(d1); } void IdDecorator::setAutoDelete(bool onOff) { if (onOff) { d2 = reinterpret_cast(IdDecorator_AutoDelete); } else { - d2 = 0; + d2 = nullptr; } } bool IdDecorator::autoDelete() const { return d2 == reinterpret_cast(IdDecorator_AutoDelete); } const ThreadWeaver::Collection *IdDecorator::collection() const { return dynamic_cast(job()); } Collection *IdDecorator::collection() { return dynamic_cast(job()); } const Sequence *IdDecorator::sequence() const { return dynamic_cast(job()); } Sequence *IdDecorator::sequence() { return dynamic_cast(job()); } } diff --git a/src/job.cpp b/src/job.cpp index b59c22c..9b83f23 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -1,213 +1,213 @@ /* -*- C++ -*- This file implements the Job class. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id: Job.cpp 20 2005-08-08 21:02:51Z mirko $ */ #include "job.h" #include "job_p.h" #include #include #include "debuggingaids.h" #include "thread.h" #include #include #include "queuepolicy.h" #include "dependencypolicy.h" #include "executor_p.h" #include "executewrapper_p.h" #include "managedjobpointer.h" #include "exception.h" namespace ThreadWeaver { Job::Job() : d_(new Private::Job_Private()) { #if !defined(NDEBUG) d()->debugExecuteWrapper.wrap(setExecutor(&(d()->debugExecuteWrapper))); #endif d()->status.storeRelease(Status_New); } Job::Job(Private::Job_Private *d__) : d_(d__) { #if !defined(NDEBUG) d()->debugExecuteWrapper.wrap(setExecutor(&(d()->debugExecuteWrapper))); #endif d()->status.storeRelease(Status_New); } Job::~Job() { for (int index = 0; index < d()->queuePolicies.size(); ++index) { d()->queuePolicies.at(index)->destructed(this); } delete d_; } void Job::execute(const JobPointer& self, Thread *th) { Executor *executor = d()->executor.loadAcquire(); Q_ASSERT(executor); //may never be unset! Q_ASSERT(self); executor->begin(self, th); self->setStatus(Status_Running); try { executor->execute(self, th); if (self->status() == Status_Running) { self->setStatus(Status_Success); } } catch (JobAborted &) { self->setStatus(Status_Aborted); } catch (JobFailed &) { self->setStatus(Status_Failed); } catch (AbortThread&) { throw; } catch (...) { TWDEBUG(0, "Uncaught exception in Job %p, aborting.", self.data()); throw; } Q_ASSERT(self->status() > Status_Running); executor->end(self, th); executor->cleanup(self, th); } void Job::blockingExecute() { - execute(ManagedJobPointer(this), 0); + execute(ManagedJobPointer(this), nullptr); } Executor *Job::setExecutor(Executor *executor) { - return d()->executor.fetchAndStoreOrdered(executor == 0 ? &Private::defaultExecutor : executor); + return d()->executor.fetchAndStoreOrdered(executor == nullptr ? &Private::defaultExecutor : executor); } Executor *Job::executor() const { return d()->executor.loadAcquire(); } int Job::priority() const { return 0; } void Job::setStatus(JobInterface::Status status) { d()->status.storeRelease(status); } JobInterface::Status Job::status() const { // since status is set only through setStatus, this should be safe: return static_cast(d()->status.loadAcquire()); } bool Job::success() const { return d()->status.loadAcquire() == Status_Success; } void Job::defaultBegin(const JobPointer&, Thread *) { } void Job::defaultEnd(const JobPointer& job, Thread *) { d()->freeQueuePolicyResources(job); } void Job::aboutToBeQueued(QueueAPI *api) { QMutexLocker l(mutex()); Q_UNUSED(l); aboutToBeQueued_locked(api); } void Job::aboutToBeQueued_locked(QueueAPI *) { } void Job::aboutToBeDequeued(QueueAPI *api) { QMutexLocker l(mutex()); Q_UNUSED(l); aboutToBeDequeued_locked(api); } void Job::aboutToBeDequeued_locked(QueueAPI *) { } void Job::assignQueuePolicy(QueuePolicy *policy) { Q_ASSERT(!mutex()->tryLock()); if (! d()->queuePolicies.contains(policy)) { d()->queuePolicies.append(policy); } } void Job::removeQueuePolicy(QueuePolicy *policy) { Q_ASSERT(!mutex()->tryLock()); int index = d()->queuePolicies.indexOf(policy); if (index != -1) { d()->queuePolicies.removeAt(index); } } QList Job::queuePolicies() const { Q_ASSERT(!mutex()->tryLock()); return d()->queuePolicies; } Private::Job_Private *Job::d() { return d_; } const Private::Job_Private *Job::d() const { return d_; } bool Job::isFinished() const { const Status s = status(); return s == Status_Success || s == Status_Failed || s == Status_Aborted; } QMutex *Job::mutex() const { return &(d()->mutex); } } #include "managedjobpointer.h" diff --git a/src/qobjectdecorator.h b/src/qobjectdecorator.h index e292fd8..8e50ac7 100644 --- a/src/qobjectdecorator.h +++ b/src/qobjectdecorator.h @@ -1,67 +1,67 @@ /* -*- C++ -*- A decorator to make jobs into QObjects in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef THREADWEAVER_QOBJECTDECORATOR_H #define THREADWEAVER_QOBJECTDECORATOR_H #include #include "threadweaver_export.h" #include "iddecorator.h" namespace ThreadWeaver { class Collection; class Sequence; class THREADWEAVER_EXPORT QObjectDecorator : public QObject, public IdDecorator { Q_OBJECT public: - explicit QObjectDecorator(JobInterface *decoratee, QObject *parent = 0); - explicit QObjectDecorator(JobInterface *decoratee, bool autoDelete, QObject *parent = 0); + explicit QObjectDecorator(JobInterface *decoratee, QObject *parent = nullptr); + explicit QObjectDecorator(JobInterface *decoratee, bool autoDelete, QObject *parent = nullptr); Q_SIGNALS: /** This signal is emitted when this job is being processed by a thread. */ void started(ThreadWeaver::JobPointer); /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ void done(ThreadWeaver::JobPointer); /** This job has failed. * * This signal is emitted when success() returns false after the job is executed. */ void failed(ThreadWeaver::JobPointer); protected: void defaultBegin(const JobPointer& job, Thread *thread) Q_DECL_OVERRIDE; void defaultEnd(const JobPointer& job, Thread *thread) Q_DECL_OVERRIDE; }; typedef QSharedPointer QJobPointer; } #endif // THREADWEAVER_QOBJECTDECORATOR_H diff --git a/src/queue.cpp b/src/queue.cpp index 204f2f5..767c8ce 100644 --- a/src/queue.cpp +++ b/src/queue.cpp @@ -1,262 +1,262 @@ /* -*- C++ -*- The Queue class in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "queue.h" #include "weaver.h" using namespace ThreadWeaver; namespace { static Queue::GlobalQueueFactory *globalQueueFactory; } class Queue::Private { public: Private(Queue *q, QueueSignals *queue) : implementation(queue) { - Q_ASSERT_X(qApp != 0, Q_FUNC_INFO, "Cannot create global ThreadWeaver instance before QApplication!"); + Q_ASSERT_X(qApp != nullptr, Q_FUNC_INFO, "Cannot create global ThreadWeaver instance before QApplication!"); Q_ASSERT(queue); queue->setParent(q); q->connect(implementation, SIGNAL(finished()), SIGNAL(finished())); q->connect(implementation, SIGNAL(suspended()), SIGNAL(suspended())); } QueueSignals *implementation; void init(QueueSignals *implementation); }; /** @brief Construct a Queue. */ Queue::Queue(QObject *parent) : QueueSignals(parent) , d(new Private(this, new Weaver)) { } /** @brief Construct a Queue, specifying the QueueSignals implementation to use. * * The QueueSignals instance is usually a Weaver object, which may be customized for specific * application needs. The Weaver instance will take ownership of the implementation object and * deletes it when destructed. * @see Weaver * @see GlobalQueueFactory */ Queue::Queue(QueueSignals *implementation, QObject *parent) : QueueSignals(parent) , d(new Private(this, implementation)) { } /** @brief Destruct the Queue object. * * If the queue is not already in Destructed state, the destructor will call shutDown() to make sure * enqueued jobs are completed and the queue is idle. * The queue implementation will be destroyed. * @see shutDown() * @see ThreadWeaver::Destructed */ Queue::~Queue() { if (d->implementation->state()->stateId() != Destructed) { d->implementation->shutDown(); } delete d->implementation; delete d; } /** @brief Create a QueueStream to enqueue jobs into this queue. */ QueueStream Queue::stream() { return QueueStream(this); } void Queue::shutDown() { d->implementation->shutDown(); } /** @brief Set the factory object that will create the global queue. * * Once set, the global queue factory will be deleted when the global ThreadWeaver pool is deleted. * The factory object needs to be set before the global ThreadWeaver pool is instantiated. Call this * method before Q(Core)Application is constructed. */ void Queue::setGlobalQueueFactory(Queue::GlobalQueueFactory *factory) { if (globalQueueFactory) { delete globalQueueFactory; } globalQueueFactory = factory; } const State *Queue::state() const { return d->implementation->state(); } namespace { class StaticThreadWeaverInstanceGuard : public QObject { Q_OBJECT public: explicit StaticThreadWeaverInstanceGuard(QAtomicPointer &instance, QCoreApplication *app) : QObject(app) , instance_(instance) { - Q_ASSERT_X(app != 0, Q_FUNC_INFO, "Calling ThreadWeaver::Weaver::instance() requires a QCoreApplication!"); + Q_ASSERT_X(app != nullptr, Q_FUNC_INFO, "Calling ThreadWeaver::Weaver::instance() requires a QCoreApplication!"); QObject *impl = instance.load()->findChild(); Q_ASSERT(impl); impl->setObjectName(QStringLiteral("GlobalQueue")); qAddPostRoutine(shutDownGlobalQueue); } ~StaticThreadWeaverInstanceGuard() { - instance_.fetchAndStoreOrdered(0); + instance_.fetchAndStoreOrdered(nullptr); delete globalQueueFactory; - globalQueueFactory = 0; + globalQueueFactory = nullptr; } private: static void shutDownGlobalQueue() { Queue::instance()->shutDown(); Q_ASSERT(Queue::instance()->state()->stateId() == Destructed); } QAtomicPointer &instance_; }; } /** @brief Access the application-global Queue. * * In some cases, the global queue is sufficient for the applications purpose. The global queue will only be * created if this method is actually called in the lifetime of the application. * * The Q(Core)Application object must exist when instance() is called for the first time. * The global queue will be destroyed when Q(Core)Application is destructed. After that, the instance() method * returns zero. */ Queue *Queue::instance() { static QAtomicPointer s_instance(globalQueueFactory ? globalQueueFactory->create(qApp) : new Queue(qApp)); //Order is of importance here: //When s_instanceGuard is destructed (first, before s_instance), it sets the value of s_instance to zero. Next, qApp will delete //the object s_instance pointed to. static StaticThreadWeaverInstanceGuard *s_instanceGuard = new StaticThreadWeaverInstanceGuard(s_instance, qApp); Q_UNUSED(s_instanceGuard); - Q_ASSERT_X(s_instance.load() == 0 || s_instance.load()->thread() == QCoreApplication::instance()->thread(), + Q_ASSERT_X(s_instance.load() == nullptr || s_instance.load()->thread() == QCoreApplication::instance()->thread(), Q_FUNC_INFO, "The global ThreadWeaver queue needs to be instantiated (accessed first) from the main thread!"); return s_instance.loadAcquire(); } void Queue::enqueue(const QVector &jobs) { d->implementation->enqueue(jobs); } void Queue::enqueue(const JobPointer &job) { enqueue(QVector() << job); } bool Queue::dequeue(const JobPointer &job) { return d->implementation->dequeue(job); } void Queue::dequeue() { return d->implementation->dequeue(); } void Queue::finish() { return d->implementation->finish(); } void Queue::suspend() { return d->implementation->suspend(); } void Queue::resume() { return d->implementation->resume(); } bool Queue::isEmpty() const { return d->implementation->isEmpty(); } bool Queue::isIdle() const { return d->implementation->isIdle(); } int Queue::queueLength() const { return d->implementation->queueLength(); } void Queue::setMaximumNumberOfThreads(int cap) { d->implementation->setMaximumNumberOfThreads(cap); } int Queue::currentNumberOfThreads() const { return d->implementation->currentNumberOfThreads(); } int Queue::maximumNumberOfThreads() const { return d->implementation->maximumNumberOfThreads(); } void Queue::requestAbort() { d->implementation->requestAbort(); } void Queue::reschedule() { d->implementation->reschedule(); } #include "queue.moc" diff --git a/src/queue.h b/src/queue.h index d037335..51e7a91 100644 --- a/src/queue.h +++ b/src/queue.h @@ -1,102 +1,102 @@ /* -*- C++ -*- The Queue class in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef THREADWEAVER_QUEUE_H #define THREADWEAVER_QUEUE_H #include #include "queuesignals.h" #include "queuestream.h" namespace ThreadWeaver { class Job; class State; /** @brief Queue implements a ThreadWeaver job queue. * * Queues process jobs enqueued in them by automatically assigning them to worker threads they manage. * Applications using ThreadWeaver can make use of a global Queue which is instantiated on demand, or * create multiple queues as needed. A job assigned to a queue will be processed by that specific queue. * * Worker threads are created by the queues as needed. To create a customized global queue, * see GlobalQueueFactory. * * @see GlobalQueueFactory * @see Queue::enqueue() * @see Queue::instance() */ class THREADWEAVER_EXPORT Queue : public QueueSignals { Q_OBJECT public: - explicit Queue(QObject *parent = 0); + explicit Queue(QObject *parent = nullptr); /** @brief Construct a queue with a customized implementation * The queue takes ownership and will delete the implementation upon destruction. */ - explicit Queue(QueueSignals *implementation, QObject *parent = 0); + explicit Queue(QueueSignals *implementation, QObject *parent = nullptr); virtual ~Queue(); QueueStream stream(); const State *state() const Q_DECL_OVERRIDE; void setMaximumNumberOfThreads(int cap) Q_DECL_OVERRIDE; int maximumNumberOfThreads() const Q_DECL_OVERRIDE; int currentNumberOfThreads() const Q_DECL_OVERRIDE; static ThreadWeaver::Queue *instance(); void enqueue(const QVector &jobs) Q_DECL_OVERRIDE; void enqueue(const JobPointer &job); bool dequeue(const JobPointer &) Q_DECL_OVERRIDE; void dequeue() Q_DECL_OVERRIDE; void finish() Q_DECL_OVERRIDE; void suspend() Q_DECL_OVERRIDE; void resume() Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; bool isIdle() const Q_DECL_OVERRIDE; int queueLength() const Q_DECL_OVERRIDE; void requestAbort() Q_DECL_OVERRIDE; void reschedule() Q_DECL_OVERRIDE; void shutDown() Q_DECL_OVERRIDE; /** @brief Interface for the global queue factory. */ struct GlobalQueueFactory { virtual ~GlobalQueueFactory() {} virtual Queue *create(QObject *parent) = 0; }; static void setGlobalQueueFactory(GlobalQueueFactory *factory); private: class Private; Private *const d; }; } #endif // THREADWEAVER_QUEUE_H diff --git a/src/queueapi.h b/src/queueapi.h index 72e12eb..e7a2e18 100644 --- a/src/queueapi.h +++ b/src/queueapi.h @@ -1,66 +1,66 @@ /* -*- C++ -*- The detailed API for Weavers in ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef QUEUEAPI_H #define QUEUEAPI_H #include "queuesignals.h" #include "weaverinterface.h" #include "state.h" #include "jobpointer.h" namespace ThreadWeaver { class QueueAPI : public QueueSignals, public WeaverInterface { Q_OBJECT public: - explicit QueueAPI(QObject *parent = 0); - explicit QueueAPI(ThreadWeaver::Private::QueueSignals_Private* d, QObject *parent = 0); + explicit QueueAPI(QObject *parent = nullptr); + explicit QueueAPI(ThreadWeaver::Private::QueueSignals_Private* d, QObject *parent = nullptr); virtual void shutDown_p() = 0; const State *state() const Q_DECL_OVERRIDE = 0; virtual State *state() = 0; virtual void setMaximumNumberOfThreads_p(int cap) = 0; virtual int maximumNumberOfThreads_p() const = 0; virtual int currentNumberOfThreads_p() const = 0; virtual bool dequeue_p(JobPointer) = 0; virtual void dequeue_p() = 0; virtual void finish_p() = 0; virtual void suspend_p() = 0; virtual void resume_p() = 0; virtual bool isEmpty_p() const = 0; virtual bool isIdle_p() const = 0; virtual int queueLength_p() const = 0; virtual void requestAbort_p() = 0; }; } #endif // QUEUEAPI_H diff --git a/src/queuesignals.cpp b/src/queuesignals.cpp index 606b9ce..082860f 100644 --- a/src/queuesignals.cpp +++ b/src/queuesignals.cpp @@ -1,68 +1,68 @@ /* -*- C++ -*- This file is part of ThreadWeaver, a KDE framework. $ Author: Mirko Boehm $ $ Copyright: (C) 2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "queuesignals.h" namespace ThreadWeaver { namespace Private { class QueueSignals_Private {}; } /** @brief Construct a QueueSignals object, passing the QObject parent. */ QueueSignals::QueueSignals(QObject *parent) : QObject(parent) - , m_d(0) + , m_d(nullptr) { } QueueSignals::QueueSignals(Private::QueueSignals_Private *d, QObject *parent) : QObject(parent) , m_d(d) { } QueueSignals::~QueueSignals() { delete m_d; - m_d = 0; + m_d = nullptr; } Private::QueueSignals_Private *QueueSignals::d() { return m_d; } const Private::QueueSignals_Private *QueueSignals::d() const { return m_d; } } diff --git a/src/queuesignals.h b/src/queuesignals.h index 5a79ac2..3277ee9 100644 --- a/src/queuesignals.h +++ b/src/queuesignals.h @@ -1,80 +1,80 @@ /* -*- C++ -*- This file is part of ThreadWeaver, a KDE framework. $ Author: Mirko Boehm $ $ Copyright: (C) 2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef THREADWEAVER_QUEUESIGNALS_H #define THREADWEAVER_QUEUESIGNALS_H #include #include "queueinterface.h" namespace ThreadWeaver { namespace Private { class QueueSignals_Private; } /** @brief QueueSignals declares the Qt signals shared by the Queue and Weaver classes. */ class QueueSignals : public QObject, public QueueInterface { Q_OBJECT public: - explicit QueueSignals(QObject *parent = 0); - explicit QueueSignals(ThreadWeaver::Private::QueueSignals_Private* d, QObject *parent = 0); + explicit QueueSignals(QObject *parent = nullptr); + explicit QueueSignals(ThreadWeaver::Private::QueueSignals_Private* d, QObject *parent = nullptr); virtual ~QueueSignals(); Q_SIGNALS: /** @brief Emitted when the Queue has completed all jobs currently queued. * * The Queue emits finished() when the the job queue is empty, and the last job currently processed by a worker threads was * completed. Beware that if multiple jobs are enqueued repeatedly one by one, this signal might be emitted multiple times, because the * queued jobs where processed before new ones could be queued. To avoid this, queue all relevant jobs in a single operation, * using for example a QueueStream or a Collection. */ void finished(); /** @brief The Queue has been suspended. * * When the Queue is suspended, worker threads will not be assigned new jobs to process. Jobs waiting in the queue will not be * started until processing is resumed. When suspend() is called, the worker threads will continue to process the job currently * assigned to them. When the last thread finishes it's current assignment, suspended() is emitted. * * @see suspend() */ void suspended(); /** @brief Emitted when the processing state of the Queue has changed. */ void stateChanged(ThreadWeaver::State*); protected: ThreadWeaver::Private::QueueSignals_Private* d(); const ThreadWeaver::Private::QueueSignals_Private* d() const; private: ThreadWeaver::Private::QueueSignals_Private* m_d; }; } #endif // THREADWEAVER_QUEUESIGNALS_H diff --git a/src/sequence_p.cpp b/src/sequence_p.cpp index 3cb25c1..f943064 100644 --- a/src/sequence_p.cpp +++ b/src/sequence_p.cpp @@ -1,107 +1,107 @@ /* -*- C++ -*- This file is part of ThreadWeaver. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sequence_p.h" #include "debuggingaids.h" namespace ThreadWeaver { namespace Private { Sequence_Private::Sequence_Private() { } BlockerPolicy *Sequence_Private::blocker() { return &blocker_; } void Sequence_Private::prepareToEnqueueElements() { Q_ASSERT(!mutex.tryLock()); const int jobs = elements.count(); // probably incorrect: completed_.storeRelease(0); // block the execution of the later jobs: for (int i = 0; i < jobs; ++i) { TWDEBUG(4, "Sequence_Private::processCompletedElement: blocking %p\n", elements.at(i).data()); JobPointer nextJob = elements.at(i); QMutexLocker l(nextJob->mutex()); nextJob->assignQueuePolicy(blocker()); } } void Sequence_Private::processCompletedElement(Collection* collection, JobPointer job, Thread*) { Q_ASSERT(!mutex.tryLock()); - Q_ASSERT(job != 0); + Q_ASSERT(job != nullptr); Q_ASSERT(!self.isNull()); const JobInterface::Status status = job->status(); if (status != JobInterface::Status_Success) { stop_locked(collection); collection->setStatus(status); } const int next = completed_.fetchAndAddAcquire(1); const int count = elements.count(); if (count > 0) { if (next < count) { TWDEBUG(4, "Sequence_Private::processCompletedElement: unblocking %p\n", elements.at(next).data()); JobPointer nextJob = elements.at(next); QMutexLocker l(nextJob->mutex()); nextJob->removeQueuePolicy(blocker()); } } } void Sequence_Private::elementDequeued(const JobPointer &job) { Q_ASSERT(!mutex.tryLock()); QMutexLocker l(job->mutex()); job->removeQueuePolicy(blocker()); } void BlockerPolicy::destructed(JobInterface*) { } bool BlockerPolicy::canRun(JobPointer) { return false; } void BlockerPolicy::free(JobPointer) { } void BlockerPolicy::release(JobPointer) { } } } diff --git a/src/thread.cpp b/src/thread.cpp index ddba76e..ce85e73 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,134 +1,134 @@ /* -*- C++ -*- This file is part of ThreadWeaver. It implements the Thread class. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id: Thread.cpp 25 2005-08-14 12:41:38Z mirko $ */ #include "thread.h" #include #include #include #include #include "threadweaver.h" #include "weaver.h" #include "job.h" #include "debuggingaids.h" #include "exception.h" using namespace ThreadWeaver; class Thread::Private { public: explicit Private(Weaver *theParent) : parent(theParent) , id(makeId()) - , job(0) + , job(nullptr) { Q_ASSERT(parent); } Weaver *parent; const unsigned int id; JobPointer job; QMutex mutex; static unsigned int makeId() { static QAtomicInt s_id(1); return s_id.fetchAndAddRelease(1); } }; Thread::Thread(Weaver *parent) : QThread() // no parent, because the QObject hierarchy of this thread // does not have a parent (see QObject::pushToThread) , d(new Private(parent)) { const QString queueName = parent->objectName().isEmpty() ? QString::fromLatin1("Queue(0x%1)").arg(quintptr(parent), 0, 16, QChar::fromLatin1('0')) : parent->objectName(); setObjectName(QString::fromLatin1("%1[%2]").arg(queueName).arg(QString::number(id()), 2, QChar::fromLatin1('0'))); } Thread::~Thread() { delete d; } unsigned int Thread::id() const { return d->id; //id is const } void Thread::run() { Q_ASSERT(d->parent); - Q_ASSERT(QCoreApplication::instance() != 0); + Q_ASSERT(QCoreApplication::instance() != nullptr); d->parent->threadEnteredRun(this); emit started(this); TWDEBUG(3, "Thread::run [%u]: running.\n", id()); bool wasBusy = false; while (true) { TWDEBUG(3, "Thread::run [%u]: trying to execute the next job.\n", id()); try { // the assignment is intentional: newJob needs to go out of scope at the end of the if statement if (JobPointer newJob = d->parent->applyForWork(this, wasBusy)) { QMutexLocker l(&d->mutex); Q_UNUSED(l); d->job = newJob; } else { break; } } catch (AbortThread&) { break; } wasBusy = true; d->job->execute(d->job, this); JobPointer oldJob; { // When finally destroying the last reference to d->job, do not hold the mutex. // It may trigger destruction of the job, which can execute arbitrary code. QMutexLocker l(&d->mutex); Q_UNUSED(l); oldJob = d->job; d->job.clear(); } oldJob.clear(); } TWDEBUG(3, "Thread::run [%u]: exiting.\n", id()); } void Thread::requestAbort() { QMutexLocker l(&d->mutex); Q_UNUSED(l); if (d->job) { d->job->requestAbort(); } } diff --git a/src/thread.h b/src/thread.h index bbf6092..d895e96 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,95 +1,95 @@ /* -*- C++ -*- This file is part of ThreadWeaver. It declares the Thread class. $ Author: Mirko Boehm $ $ Copyright: (C) 2004-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef THREADWEAVER_THREAD_H #define THREADWEAVER_THREAD_H #include #include #include "threadweaver_export.h" #include "jobpointer.h" namespace ThreadWeaver { class Job; class Weaver; /** @brief Thread represents a worker thread in a Queue's inventory. * * Threads are created and managed by queues on demand. A Thread will try to retrieve and process * jobs from the queue until it is told to exit. */ class THREADWEAVER_EXPORT Thread : public QThread { Q_OBJECT public: /** @brief Create a thread. * * @param parent the parent Weaver */ - explicit Thread(Weaver *parent = 0); + explicit Thread(Weaver *parent = nullptr); /** The destructor. */ ~Thread(); /** @brief The run method is reimplemented to execute jobs from the queue. * * Whenever the thread is idle, it will ask its Weaver parent for a Job to do. The Weaver will either return a Job or a null * pointer. When a null pointer is returned, it tells the thread to exit. */ void run() Q_DECL_OVERRIDE; /** @brief Returns the thread id. * * This id marks the respective Thread object, and must therefore not be confused with, e.g., the pthread thread ID. * The way threads are implemented and identified is platform specific. id() is the only way to uniquely identify a thread * within ThreadWeaver. */ unsigned int id() const; /** @brief Request the abortion of the job that is processed currently. * * If there is no current job, this method will do nothing, but can safely be called. It forwards the request to the * current Job. */ void requestAbort(); Q_SIGNALS: //FIXME needed? /** The thread has been started. */ void started(ThreadWeaver::Thread *); private: class Private; Private *const d; }; } #endif diff --git a/src/weaver.cpp b/src/weaver.cpp index 8c52050..99e8ec1 100644 --- a/src/weaver.cpp +++ b/src/weaver.cpp @@ -1,597 +1,597 @@ /* -*- C++ -*- This file implements the WeaverImpl class. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id: WeaverImpl.cpp 30 2005-08-16 16:16:04Z mirko $ */ #include "weaver.h" #include #include #include #include #include "job.h" #include "managedjobpointer.h" #include "state.h" #include "thread.h" #include "threadweaver.h" #include "debuggingaids.h" #include "queuepolicy.h" #include "weaver_p.h" #include "suspendedstate.h" #include "suspendingstate.h" #include "destructedstate.h" #include "workinghardstate.h" #include "shuttingdownstate.h" #include "inconstructionstate.h" #include "exception.h" using namespace ThreadWeaver; /** @brief Constructs a Weaver object. */ Weaver::Weaver(QObject *parent) : QueueAPI(new Private::Weaver_Private(), parent) { qRegisterMetaType("ThreadWeaver::JobPointer"); QMutexLocker l(d()->mutex); Q_UNUSED(l); // initialize state objects: d()->states[InConstruction] = QSharedPointer(new InConstructionState(this)); setState_p(InConstruction); d()->states[WorkingHard] = QSharedPointer(new WorkingHardState(this)); d()->states[Suspending] = QSharedPointer(new SuspendingState(this)); d()->states[Suspended] = QSharedPointer(new SuspendedState(this)); d()->states[ShuttingDown] = QSharedPointer(new ShuttingDownState(this)); d()->states[Destructed] = QSharedPointer(new DestructedState(this)); setState_p(WorkingHard); } /** @brief Destructs a Weaver object. */ Weaver::~Weaver() { Q_ASSERT_X(state()->stateId() == Destructed, Q_FUNC_INFO, "shutDown() method was not called before Weaver destructor!"); } /** @brief Enter Destructed state. * * Once this method returns, it is save to delete this object. */ void Weaver::shutDown() { state()->shutDown(); } void Weaver::shutDown_p() { // the constructor may only be called from the thread that owns this // object (everything else would be what we professionals call "insane") REQUIRE(QThread::currentThread() == thread()); TWDEBUG(3, "WeaverImpl::shutDown: destroying inventory.\n"); d()->semaphore.acquire(d()->createdThreads.loadAcquire()); finish(); suspend(); setState(ShuttingDown); reschedule(); d()->jobFinished.wakeAll(); // problem: Some threads might not be asleep yet, just finding // out if a job is available. Those threads will suspend // waiting for their next job (a rare case, but not impossible). // Therefore, if we encounter a thread that has not exited, we // have to wake it again (which we do in the following for // loop). for (;;) { - Thread *th = 0; + Thread *th = nullptr; { QMutexLocker l(d()->mutex); Q_UNUSED(l); if (d()->inventory.isEmpty()) { break; } th = d()->inventory.takeFirst(); } if (!th->isFinished()) { for (;;) { Q_ASSERT(state()->stateId() == ShuttingDown); reschedule(); if (th->wait(100)) { break; } TWDEBUG(1, "WeaverImpl::shutDown: thread %i did not exit as expected, " "retrying.\n", th->id()); } } emit(threadExited(th)); delete th; } Q_ASSERT(d()->inventory.isEmpty()); TWDEBUG(3, "WeaverImpl::shutDown: done\n"); setState(Destructed); // Destructed ignores all calls into the queue API } /** @brief Set the Weaver state. * @see StateId * @see WeaverImplState * @see State */ void Weaver::setState(StateId id) { QMutexLocker l(d()->mutex); Q_UNUSED(l); setState_p(id); } void Weaver::setState_p(StateId id) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called State *newState = d()->states[id].data(); State *previous = d()->state.fetchAndStoreOrdered(newState); - if (previous == 0 || previous->stateId() != id) { + if (previous == nullptr || previous->stateId() != id) { newState->activated(); TWDEBUG(2, "WeaverImpl::setState: state changed to \"%s\".\n", newState->stateName().toLatin1().constData()); if (id == Suspended) { emit(suspended()); } emit(stateChanged(newState)); } } const State *Weaver::state() const { return d()->state.loadAcquire(); } State *Weaver::state() { return d()->state.loadAcquire(); } void Weaver::setMaximumNumberOfThreads(int cap) { Q_ASSERT_X(cap >= 0, "Weaver Impl", "Thread inventory size has to be larger than or equal to zero."); QMutexLocker l(d()->mutex); Q_UNUSED(l); state()->setMaximumNumberOfThreads(cap); reschedule(); } void Weaver::setMaximumNumberOfThreads_p(int cap) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called const bool createInitialThread = (d()->inventoryMax == 0 && cap > 0); d()->inventoryMax = cap; if (createInitialThread) { adjustInventory(1); } } int Weaver::maximumNumberOfThreads() const { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->maximumNumberOfThreads(); } int Weaver::maximumNumberOfThreads_p() const { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called return d()->inventoryMax; } int Weaver::currentNumberOfThreads() const { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->currentNumberOfThreads(); } int Weaver::currentNumberOfThreads_p() const { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called return d()->inventory.count(); } void Weaver::enqueue(const QVector &jobs) { QMutexLocker l(d()->mutex); Q_UNUSED(l); state()->enqueue(jobs); } void Weaver::enqueue_p(const QVector &jobs) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called if (jobs.isEmpty()) { return; } Q_FOREACH (const JobPointer &job, jobs) { if (job) { Q_ASSERT(job->status() == Job::Status_New); adjustInventory(jobs.size()); TWDEBUG(3, "WeaverImpl::enqueue: queueing job %p.\n", (void *)job.data()); job->aboutToBeQueued(this); // find position for insertion: int i = d()->assignments.size(); if (i > 0) { while (i > 0 && d()->assignments.at(i - 1)->priority() < job->priority()) { --i; } d()->assignments.insert(i, job); } else { d()->assignments.append(job); } job->setStatus(Job::Status_Queued); reschedule(); } } } bool Weaver::dequeue(const JobPointer &job) { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->dequeue(job); } bool Weaver::dequeue_p(JobPointer job) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called int position = d()->assignments.indexOf(job); if (position != -1) { job->aboutToBeDequeued(this); int newPosition = d()->assignments.indexOf(job); JobPointer job = d()->assignments.takeAt(newPosition); job->setStatus(Job::Status_New); Q_ASSERT(!d()->assignments.contains(job)); TWDEBUG(3, "WeaverImpl::dequeue: job %p dequeued, %i jobs left.\n", (void *)job.data(), queueLength_p()); // from the queues point of view, a job is just as finished if it gets dequeued: d()->jobFinished.wakeAll(); Q_ASSERT(!d()->assignments.contains(job)); return true; } else { TWDEBUG(3, "WeaverImpl::dequeue: job %p not found in queue.\n", (void *)job.data()); return false; } } void Weaver::dequeue() { QMutexLocker l(d()->mutex); Q_UNUSED(l); state()->dequeue(); } void Weaver::dequeue_p() { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called TWDEBUG(3, "WeaverImpl::dequeue: dequeueing all jobs.\n"); for (int index = 0; index < d()->assignments.size(); ++index) { d()->assignments.at(index)->aboutToBeDequeued(this); } d()->assignments.clear(); ENSURE(d()->assignments.isEmpty()); } void Weaver::finish() { QMutexLocker l(d()->mutex); Q_UNUSED(l); state()->finish(); } void Weaver::finish_p() { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called #ifdef QT_NO_DEBUG const int MaxWaitMilliSeconds = 50; #else const int MaxWaitMilliSeconds = 500; #endif while (!isIdle_p()) { Q_ASSERT_X(state()->stateId() == WorkingHard, Q_FUNC_INFO, qPrintable(state()->stateName())); TWDEBUG(2, "WeaverImpl::finish: not done, waiting.\n"); if (d()->jobFinished.wait(d()->mutex, MaxWaitMilliSeconds) == false) { TWDEBUG(2, "WeaverImpl::finish: wait timed out, %i jobs left, waking threads.\n", queueLength_p()); reschedule(); } } TWDEBUG(2, "WeaverImpl::finish: done.\n\n\n"); } void Weaver::suspend() { //FIXME? //QMutexLocker l(m_mutex); Q_UNUSED(l); state()->suspend(); } void Weaver::suspend_p() { //FIXME ? } void Weaver::resume() { //FIXME? //QMutexLocker l(m_mutex); Q_UNUSED(l); state()->resume(); } void Weaver::resume_p() { //FIXME ? } bool Weaver::isEmpty() const { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->isEmpty(); } bool Weaver::isEmpty_p() const { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called return d()->assignments.isEmpty(); } bool Weaver::isIdle() const { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->isIdle(); } bool Weaver::isIdle_p() const { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called return isEmpty_p() && d()->active == 0; } int Weaver::queueLength() const { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->queueLength(); } int Weaver::queueLength_p() const { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called return d()->assignments.count(); } void Weaver::requestAbort() { QMutexLocker l(d()->mutex); Q_UNUSED(l); return state()->requestAbort(); } void Weaver::reschedule() { d()->jobAvailable.wakeAll(); } void Weaver::requestAbort_p() { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called for (int i = 0; i inventory.size(); ++i) { d()->inventory[i]->requestAbort(); } } /** @brief Adjust the inventory size. * * Requires that the mutex is being held when called. * * This method creates threads on demand. Threads in the inventory * are not created upon construction of the WeaverImpl object, but * when jobs are queued. This avoids costly delays on the application * startup time. Threads are created when the inventory size is under * inventoryMin and new jobs are queued. */ void Weaver::adjustInventory(int numberOfNewJobs) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called //number of threads that can be created: const int reserve = d()->inventoryMax - d()->inventory.count(); if (reserve > 0) { for (int i = 0; i < qMin(reserve, numberOfNewJobs); ++i) { Thread *th = createThread(); th->moveToThread(th); // be sane from the start d()->inventory.append(th); th->start(); d()->createdThreads.ref(); TWDEBUG(2, "WeaverImpl::adjustInventory: thread created, " "%i threads in inventory.\n", currentNumberOfThreads_p()); } } } Private::Weaver_Private *Weaver::d() { return reinterpret_cast(QueueSignals::d()); } const Private::Weaver_Private *Weaver::d() const { return reinterpret_cast(QueueSignals::d()); } /** @brief Factory method to create the threads. * * Overload in adapted Weaver implementations. */ Thread *Weaver::createThread() { return new Thread(this); } /** @brief Increment the count of active threads. */ void Weaver::incActiveThreadCount() { adjustActiveThreadCount(1); } /** brief Decrement the count of active threads. */ void Weaver::decActiveThreadCount() { adjustActiveThreadCount(-1); // the done job could have freed another set of jobs, and we do not know how // many - therefore we need to wake all threads: d()->jobFinished.wakeAll(); } /** @brief Adjust active thread count. * * This is a helper function for incActiveThreadCount and decActiveThreadCount. */ void Weaver::adjustActiveThreadCount(int diff) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called d()->active += diff; TWDEBUG(4, "WeaverImpl::adjustActiveThreadCount: %i active threads (%i jobs" " in queue).\n", d()->active, queueLength_p()); if (d()->assignments.isEmpty() && d()->active == 0) { P_ASSERT(diff < 0); // cannot reach zero otherwise emit(finished()); } } /** @brief Returns the number of active threads. * * Threads are active if they process a job. Requires that the mutex is being held when called. */ int Weaver::activeThreadCount() { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called return d()->active; } /** @brief Called from a new thread when entering the run method. */ void Weaver::threadEnteredRun(Thread *thread) { d()->semaphore.release(1); emit threadStarted(thread); } /** @brief Take the first available job out of the queue and return it. * * The job will be removed from the queue (therefore, take). Only jobs that have no unresolved dependencies * are considered available. If only jobs that depened on other unfinished jobs are in the queue, this method * blocks on m_jobAvailable. * * This method will enter suspended state if the active thread count is now zero and * suspendIfAllThreadsInactive is true. * If justReturning is true, do not assign a new job, just process the completed previous one. */ JobPointer Weaver::takeFirstAvailableJobOrSuspendOrWait(Thread *th, bool threadWasBusy, bool suspendIfInactive, bool justReturning) { QMutexLocker l(d()->mutex); Q_UNUSED(l); Q_ASSERT(threadWasBusy == false || (threadWasBusy == true && d()->active > 0)); TWDEBUG(3, "WeaverImpl::takeFirstAvailableJobOrWait: trying to assign new job to thread %i (%s state).\n", th->id(), qPrintable(state()->stateName())); TWDEBUG(5, "WeaverImpl::takeFirstAvailableJobOrWait: %i active threads, was busy: %s, suspend: %s, assign new job: %s.\n", activeThreadCount(), threadWasBusy ? "yes" : "no", suspendIfInactive ? "yes" : "no", !justReturning ? "yes" : "no"); d()->deleteExpiredThreads(); adjustInventory(1); if (threadWasBusy) { // cleanup and send events: decActiveThreadCount(); } Q_ASSERT(d()->active >= 0); if (suspendIfInactive && d()->active == 0 && state()->stateId() == Suspending) { setState_p(Suspended); return JobPointer(); } if (state()->stateId() != WorkingHard || justReturning) { return JobPointer(); } if (state()->stateId() == WorkingHard && d()->inventory.size() > d()->inventoryMax) { const int count = d()->inventory.removeAll(th); Q_ASSERT(count == 1); d()->expiredThreads.append(th); throw AbortThread(tr("Inventory size exceeded")); } JobPointer next; for (int index = 0; index < d()->assignments.size(); ++index) { const JobPointer &candidate = d()->assignments.at(index); if (d()->canBeExecuted(candidate)) { next = candidate; d()->assignments.removeAt(index); break; } } if (next) { incActiveThreadCount(); TWDEBUG(3, "WeaverImpl::takeFirstAvailableJobOrWait: job %p assigned to thread %i (%s state).\n", next.data(), th->id(), qPrintable(state()->stateName())); return next; } blockThreadUntilJobsAreBeingAssigned_locked(th); return JobPointer(); } /** @brief Assign a job to the calling thread. * * This is supposed to be called from the Thread objects in the inventory. Do not call this method from * your code. * Returns 0 if the weaver is shutting down, telling the calling thread to finish and exit. If no jobs are * available and shut down is not in progress, the calling thread is suspended until either condition is met. * @param wasBusy True if the thread is returning from processing a job */ JobPointer Weaver::applyForWork(Thread *th, bool wasBusy) { return state()->applyForWork(th, wasBusy); } /** @brief Wait for a job to become available. */ void Weaver::waitForAvailableJob(Thread *th) { state()->waitForAvailableJob(th); } /** @brief Blocks the calling thread until jobs can be assigned. */ void Weaver::blockThreadUntilJobsAreBeingAssigned(Thread *th) { QMutexLocker l(d()->mutex); Q_UNUSED(l); blockThreadUntilJobsAreBeingAssigned_locked(th); } /** @brief Blocks the calling thread until jobs can be assigned. * * The mutex must be held when calling this method. */ void Weaver::blockThreadUntilJobsAreBeingAssigned_locked(Thread *th) { Q_ASSERT(!d()->mutex->tryLock()); //mutex has to be held when this method is called TWDEBUG(4, "WeaverImpl::blockThreadUntilJobsAreBeingAssigned_locked: thread %i blocked (%s state).\n", th->id(), qPrintable(state()->stateName())); emit threadSuspended(th); d()->jobAvailable.wait(d()->mutex); TWDEBUG(4, "WeaverImpl::blockThreadUntilJobsAreBeingAssigned_locked: thread %i resumed (%s state).\n", th->id(), qPrintable(state()->stateName())); } diff --git a/src/weaver.h b/src/weaver.h index 6659334..65dc324 100644 --- a/src/weaver.h +++ b/src/weaver.h @@ -1,129 +1,129 @@ /* -*- C++ -*- This file implements the public interfaces of the WeaverImpl class. $ Author: Mirko Boehm $ $ Copyright: (C) 2005-2013 Mirko Boehm $ $ Contact: mirko@kde.org http://www.kde.org http://creative-destruction.me $ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WeaverImpl_H #define WeaverImpl_H #include #include "queueapi.h" namespace ThreadWeaver { class State; class Job; class Thread; class WeaverImplState; class SuspendingState; namespace Private { class Weaver_Private; } /** @brief A Weaver manages worker threads. * * It creates an inventory of Thread objects to which it assigns jobs from its queue. * It extends the API of Queue, hiding methods that need to be public to implement state handling, but * should not be exposed in general. */ class THREADWEAVER_EXPORT Weaver : public QueueAPI { Q_OBJECT public: - explicit Weaver(QObject *parent = 0); + explicit Weaver(QObject *parent = nullptr); virtual ~Weaver(); void shutDown() Q_DECL_OVERRIDE; void shutDown_p() Q_DECL_OVERRIDE; const State *state() const Q_DECL_OVERRIDE; State *state() Q_DECL_OVERRIDE; void setMaximumNumberOfThreads(int cap) Q_DECL_OVERRIDE; int maximumNumberOfThreads() const Q_DECL_OVERRIDE; int currentNumberOfThreads() const Q_DECL_OVERRIDE; void setState(StateId); void enqueue(const QVector &jobs) Q_DECL_OVERRIDE; bool dequeue(const JobPointer &job) Q_DECL_OVERRIDE; void dequeue() Q_DECL_OVERRIDE; void finish() Q_DECL_OVERRIDE; void suspend() Q_DECL_OVERRIDE; void resume() Q_DECL_OVERRIDE; bool isEmpty() const Q_DECL_OVERRIDE; bool isIdle() const Q_DECL_OVERRIDE; int queueLength() const Q_DECL_OVERRIDE; JobPointer applyForWork(Thread *thread, bool wasBusy) Q_DECL_OVERRIDE; void waitForAvailableJob(Thread *th) Q_DECL_OVERRIDE; void blockThreadUntilJobsAreBeingAssigned(Thread *th); void blockThreadUntilJobsAreBeingAssigned_locked(Thread *th); void incActiveThreadCount(); void decActiveThreadCount(); int activeThreadCount(); void threadEnteredRun(Thread *thread); JobPointer takeFirstAvailableJobOrSuspendOrWait(Thread *th, bool threadWasBusy, bool suspendIfAllThreadsInactive, bool justReturning); void requestAbort() Q_DECL_OVERRIDE; void reschedule() Q_DECL_OVERRIDE; //FIXME: rename _p to _locked: friend class WeaverImplState; friend class SuspendingState; void setState_p(StateId); void setMaximumNumberOfThreads_p(int cap) Q_DECL_OVERRIDE; int maximumNumberOfThreads_p() const Q_DECL_OVERRIDE; int currentNumberOfThreads_p() const Q_DECL_OVERRIDE; void enqueue_p(const QVector &jobs); bool dequeue_p(JobPointer job) Q_DECL_OVERRIDE; void dequeue_p() Q_DECL_OVERRIDE; void finish_p() Q_DECL_OVERRIDE; void suspend_p() Q_DECL_OVERRIDE; void resume_p() Q_DECL_OVERRIDE; bool isEmpty_p() const Q_DECL_OVERRIDE; bool isIdle_p() const Q_DECL_OVERRIDE; int queueLength_p() const Q_DECL_OVERRIDE; void requestAbort_p() Q_DECL_OVERRIDE; Q_SIGNALS: /** @brief A Thread has been created. */ void threadStarted(ThreadWeaver::Thread *); /** @brief A thread has exited. */ void threadExited(ThreadWeaver::Thread *); /** @brief A thread has been suspended. */ void threadSuspended(ThreadWeaver::Thread *); protected: void adjustActiveThreadCount(int diff); virtual Thread *createThread(); void adjustInventory(int noOfNewJobs); private: ThreadWeaver::Private::Weaver_Private* d(); const ThreadWeaver::Private::Weaver_Private* d() const; }; } // namespace ThreadWeaver #endif // WeaverImpl_H