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"