File indexing completed on 2024-04-28 15:34:47

0001 /* -*- C++ -*-
0002     This file contains a testsuite for the memory management in ThreadWeaver.
0003 
0004     SPDX-FileCopyrightText: 2005-2013 Mirko Boehm <mirko@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include <QEventLoop>
0010 
0011 #include "DeleteTest.h"
0012 
0013 #include <ThreadWeaver/DebuggingAids>
0014 #include <ThreadWeaver/QObjectDecorator>
0015 #include <ThreadWeaver/Sequence>
0016 #include <ThreadWeaver/ThreadWeaver>
0017 
0018 #include "AppendCharacterJob.h"
0019 
0020 class InstanceCountingBusyJob : public BusyJob
0021 {
0022 public:
0023     explicit InstanceCountingBusyJob()
0024         : BusyJob()
0025     {
0026         instances_.fetchAndAddAcquire(1);
0027     }
0028 
0029     ~InstanceCountingBusyJob() override
0030     {
0031         instances_.fetchAndAddAcquire(-1);
0032     }
0033 
0034     static int instances()
0035     {
0036         return instances_.loadAcquire();
0037     }
0038 
0039 private:
0040     static QAtomicInt instances_;
0041 };
0042 
0043 QAtomicInt InstanceCountingBusyJob::instances_;
0044 
0045 class InstanceCountingCollection : public Collection
0046 {
0047 public:
0048     explicit InstanceCountingCollection()
0049         : Collection()
0050     {
0051         instances_.fetchAndAddAcquire(1);
0052     }
0053 
0054     ~InstanceCountingCollection() override
0055     {
0056         instances_.fetchAndAddAcquire(-1);
0057     }
0058 
0059     static int instances()
0060     {
0061         return instances_.loadAcquire();
0062     }
0063 
0064 private:
0065     static QAtomicInt instances_;
0066 };
0067 
0068 QAtomicInt InstanceCountingCollection::instances_;
0069 
0070 class InstanceCountingSequence : public Sequence
0071 {
0072 public:
0073     explicit InstanceCountingSequence()
0074         : Sequence()
0075     {
0076         instances_.fetchAndAddAcquire(1);
0077     }
0078 
0079     ~InstanceCountingSequence() override
0080     {
0081         instances_.fetchAndAddAcquire(-1);
0082     }
0083 
0084     static int instances()
0085     {
0086         return instances_.loadAcquire();
0087     }
0088 
0089 private:
0090     static QAtomicInt instances_;
0091 };
0092 
0093 QAtomicInt InstanceCountingSequence::instances_;
0094 
0095 DeleteTest::DeleteTest()
0096 {
0097     ThreadWeaver::setDebugLevel(true, 1);
0098 }
0099 
0100 void DeleteTest::DeleteJobsTest()
0101 {
0102     const int NumberOfJobs = 100;
0103     ThreadWeaver::Queue queue;
0104     //    queue.setMaximumNumberOfThreads(1);
0105     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0106     { // just to be sure instance counting works:
0107         InstanceCountingBusyJob job;
0108         Q_UNUSED(job);
0109         QCOMPARE(InstanceCountingBusyJob::instances(), 1);
0110     }
0111     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0112     queue.suspend();
0113     for (int i = 0; i < NumberOfJobs; ++i) {
0114         queue.stream() << new InstanceCountingBusyJob;
0115     }
0116     queue.resume();
0117     queue.finish();
0118     // The used Weaver instance needs to be shut down. The threads may still hold a reference to the previous job while
0119     // waiting for the next one or blocking because the queue is empty. If all threads have exited, no references to any jobs are
0120     // held anymore.
0121     queue.shutDown();
0122     TWDEBUG(3, "DeleteTest::DeleteJobsTest: instances: %i\n", InstanceCountingBusyJob::instances());
0123     // QCOMPARE(InstanceCountingBusyJob::instances(), NumberOfJobs); // held by signals about the job being started and finished
0124     QCOMPARE(InstanceCountingBusyJob::instances(), 0); // zero, since threads are not sending the jobStarted/jobDone signals anymore
0125     qApp->processEvents();
0126     TWDEBUG(3, "DeleteTest::DeleteJobsTest: instances: %i\n", InstanceCountingBusyJob::instances());
0127     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0128 }
0129 
0130 void DeleteTest::MutexLockingAssertsTest()
0131 {
0132     QMutex mutex;
0133     MUTEX_ASSERT_UNLOCKED(&mutex);
0134     mutex.lock();
0135     MUTEX_ASSERT_LOCKED(&mutex);
0136     mutex.unlock();
0137     MUTEX_ASSERT_UNLOCKED(&mutex);
0138 }
0139 
0140 void DeleteTest::DeleteCollectionTest()
0141 {
0142     const int NumberOfCollections = 100;
0143     const int NumberOfJobs = 10;
0144     ThreadWeaver::Queue queue;
0145     //    queue.setMaximumNumberOfThreads(1);
0146     QCOMPARE(InstanceCountingCollection::instances(), 0);
0147     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0148     queue.suspend();
0149     JobPointer temp; // for debugging
0150     for (int i = 0; i < NumberOfCollections; ++i) {
0151         QSharedPointer<InstanceCountingCollection> col(new InstanceCountingCollection);
0152         if (i == 0) {
0153             temp = col;
0154         }
0155         for (int j = 0; j < NumberOfJobs; ++j) {
0156             col->addJob(JobPointer(new InstanceCountingBusyJob));
0157         }
0158         queue.enqueue(col);
0159     }
0160     queue.resume();
0161     queue.finish();
0162     // The used Weaver instance needs to be shut down. The threads may still hold a reference to the previous job while
0163     // waiting for the next one or blocking because the queue is empty. If all threads have exited, no references to any jobs are
0164     // held anymore.
0165     queue.shutDown();
0166 
0167     TWDEBUG(3,
0168             "DeleteTest::DeleteJobsTest: collection instances: %i, job instances: %i\n",
0169             InstanceCountingCollection::instances(),
0170             InstanceCountingBusyJob::instances());
0171     // held by signals about the job being started and finished:
0172     // QCOMPARE(InstanceCountingCollection::instances(), NumberOfCollections);
0173     // one, since threads are not sending the jobStarted/jobDone signals anymore:
0174     QCOMPARE(InstanceCountingCollection::instances(), 1);
0175     qApp->processEvents();
0176     TWDEBUG(3,
0177             "DeleteTest::DeleteJobsTest: collection instances: %i, job instances: %i\n",
0178             InstanceCountingCollection::instances(),
0179             InstanceCountingBusyJob::instances());
0180     // these are held in temp:
0181     QCOMPARE(InstanceCountingBusyJob::instances(), NumberOfJobs);
0182     QCOMPARE(InstanceCountingCollection::instances(), 1);
0183     temp.clear();
0184     QCOMPARE(InstanceCountingCollection::instances(), 0);
0185     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0186 }
0187 
0188 void DeleteTest::DeleteDecoratedCollectionTest()
0189 {
0190     const int NumberOfCollections = 100;
0191     const int NumberOfJobs = 10;
0192     m_finishCount.storeRelease(0);
0193     ThreadWeaver::Queue queue;
0194     //    queue.setMaximumNumberOfThreads(1);
0195     QCOMPARE(InstanceCountingCollection::instances(), 0);
0196     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0197     queue.suspend();
0198     JobPointer temp; // for debugging
0199     for (int i = 0; i < NumberOfCollections; ++i) {
0200         QJobPointer col(new QObjectDecorator(new InstanceCountingCollection));
0201         if (i == 0) {
0202             temp = col;
0203         }
0204         for (int j = 0; j < NumberOfJobs; ++j) {
0205             col->collection()->addJob(JobPointer(new InstanceCountingBusyJob));
0206         }
0207         QVERIFY(connect(col.data(), SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(countCompletedDecoratedCollection(ThreadWeaver::JobPointer))));
0208 
0209         queue.enqueue(col);
0210         m_finishCount.fetchAndAddRelease(1);
0211     }
0212     QCOMPARE(m_finishCount.loadAcquire(), NumberOfCollections);
0213     QEventLoop loop;
0214     QVERIFY(connect(this, SIGNAL(deleteDecoratedCollectionTestCompleted()), &loop, SLOT(quit()), Qt::QueuedConnection));
0215     queue.resume();
0216     queue.finish();
0217     loop.exec();
0218     QCOMPARE(m_finishCount.loadAcquire(), 0);
0219     // The used Weaver instance needs to be shut down. The threads may still hold a reference to the previous job while
0220     // waiting for the next one or blocking because the queue is empty. If all threads have exited, no references to any jobs are
0221     // held anymore.
0222     queue.shutDown();
0223 
0224     TWDEBUG(3, "DeleteTest::DeleteJobsTest: instances: %i\n", InstanceCountingCollection::instances());
0225     QCOMPARE(InstanceCountingCollection::instances(), 1); // held in temp
0226     temp.clear();
0227     TWDEBUG(3, "DeleteTest::DeleteJobsTest: instances: %i\n", InstanceCountingCollection::instances());
0228     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0229     QCOMPARE(InstanceCountingCollection::instances(), 0);
0230 }
0231 
0232 void DeleteTest::DeleteSequenceTest()
0233 {
0234     const int NumberOfSequences = 100;
0235     const int NumberOfJobs = 10;
0236     m_finishCount.storeRelease(0);
0237     ThreadWeaver::Queue queue;
0238     //    queue.setMaximumNumberOfThreads(1);
0239     QCOMPARE(InstanceCountingSequence::instances(), 0);
0240     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0241     queue.suspend();
0242     for (int i = 0; i < NumberOfSequences; ++i) {
0243         QJobPointer seq(new QObjectDecorator(new InstanceCountingSequence));
0244         for (int j = 0; j < NumberOfJobs; ++j) {
0245             seq->sequence()->addJob(JobPointer(new InstanceCountingBusyJob));
0246         }
0247         QVERIFY(connect(seq.data(), SIGNAL(done(ThreadWeaver::JobPointer)), SLOT(deleteSequence(ThreadWeaver::JobPointer))));
0248 
0249         queue.enqueue(seq);
0250         m_finishCount.fetchAndAddRelease(1);
0251     }
0252     QCOMPARE(m_finishCount.loadAcquire(), NumberOfSequences);
0253     QEventLoop loop;
0254     QVERIFY(connect(this, SIGNAL(deleteSequenceTestCompleted()), &loop, SLOT(quit()), Qt::QueuedConnection));
0255     queue.resume();
0256     queue.finish();
0257     loop.exec();
0258     QCOMPARE(m_finishCount.loadAcquire(), 0);
0259     // The used Weaver instance needs to be shut down. The threads may still hold a reference to the previous job while
0260     // waiting for the next one or blocking because the queue is empty. If all threads have exited, no references to any jobs are
0261     // held anymore.
0262     queue.shutDown();
0263     QCOMPARE(InstanceCountingBusyJob::instances(), 0);
0264     QCOMPARE(InstanceCountingSequence::instances(), 0);
0265 }
0266 
0267 void DeleteTest::deleteSequence(ThreadWeaver::JobPointer)
0268 {
0269     if (m_finishCount.fetchAndAddRelease(-1) == 1) { // if it *was* 1...
0270         Q_EMIT deleteSequenceTestCompleted();
0271     }
0272 }
0273 
0274 void DeleteTest::countCompletedDecoratedCollection(JobPointer)
0275 {
0276     if (m_finishCount.fetchAndAddRelease(-1) == 1) { // if it *was* 1...
0277         Q_EMIT deleteDecoratedCollectionTestCompleted();
0278     }
0279 }
0280 
0281 QMutex s_GlobalMutex;
0282 
0283 QTEST_MAIN(DeleteTest)
0284 
0285 #include "moc_DeleteTest.cpp"