1 /* 2 $Id: ConcurrencyTest2.cpp 383 2002-07-22 09:28:00Z tylerdauwalder $ 3 4 This file implements a test class for testing BMessageQueue functionality. 5 It tests use cases Destruction, Add Message 3, Remove Message 2, 6 Next Message 2, Lock 1, Lock 2, Unlock. 7 8 The test works like the following: 9 - It does the test two times, one unlocking using Unlock() and the other 10 unlocking using delete. 11 - It populates the queue with numAddMessages messages to start. 12 - The queue is locked 13 - It starts four threads. 14 - In one thread, a NextMessage() blocks 15 - In the second thread, a RemoveMessage() blocks 16 - In the third thread, an AddMessage() blocks 17 - In the fourth thread, a Lock() blocks 18 - After a short snooze, the queue is released using Unlock() or delete. 19 - Each of the four threads wake up and each checks as best it can that it 20 was successful and did not violate mutual exclusion. 21 22 */ 23 24 25 #include "ThreadedTestCaller.h" 26 #include "ConcurrencyTest2.h" 27 #include <MessageQueue.h> 28 29 30 // This constant indicates the number of messages to add to the queue. 31 const int numAddMessages = 50; 32 33 // This constant is used as a base amount of time to snooze. 34 bigtime_t SNOOZE_TIME = 100000; 35 36 37 /* 38 * Method: ConcurrencyTest2::ConcurrencyTest2() 39 * Descr: This is the constructor for this test. 40 */ 41 42 43 ConcurrencyTest2::ConcurrencyTest2(std::string name, bool unlockFlag) : 44 MessageQueueTestCase(name), unlockTest(unlockFlag) 45 { 46 } 47 48 49 /* 50 * Method: ConcurrencyTest2::~ConcurrencyTest2() 51 * Descr: This is the destructor for this test. 52 */ 53 54 55 ConcurrencyTest2::~ConcurrencyTest2() 56 { 57 } 58 59 60 /* 61 * Method: ConcurrencyTest2::setUp() 62 * Descr: This member functions sets the environment for the test. 63 * it sets the lock flag and resets the message destructor 64 * count. Finally, it adds numAddMessages messages to the 65 * queue. 66 */ 67 68 69 void ConcurrencyTest2::setUp(void) 70 { 71 isLocked = false; 72 testMessageClass::messageDestructorCount = 0; 73 74 int i; 75 BMessage *theMessage; 76 for (i=0; i < numAddMessages; i++) { 77 theMessage = new testMessageClass(i); 78 theMessageQueue->AddMessage(theMessage); 79 } 80 removeMessage = theMessage; 81 } 82 83 84 /* 85 * Method: ConcurrencyTest2::TestThread1() 86 * Descr: This member function is one thread within the test. It 87 * acquires the lock on the queue and then sleeps for a while. 88 * When it wakes up, it releases the lock on the queue by 89 * either doing an Unlock() or deleting the queue. 90 */ 91 92 93 void ConcurrencyTest2::TestThread1(void) 94 { 95 theMessageQueue->Lock(); 96 isLocked = true; 97 98 snooze(SNOOZE_TIME); 99 100 isLocked = false; 101 if (unlockTest) { 102 theMessageQueue->Unlock(); 103 } else { 104 BMessageQueue *tmpMessageQueue = theMessageQueue; 105 theMessageQueue = NULL; 106 delete tmpMessageQueue; 107 } 108 } 109 110 111 /* 112 * Method: ConcurrencyTest2::TestThread2() 113 * Descr: This member function is one thread within the test. It 114 * snoozes for a short time so that TestThread1() will grab 115 * the lock. If this is the delete test, this thread 116 * terminates since Be's implementation may corrupt memory. 117 * Otherwise, the thread blocks attempting a NextMessage() on 118 * the queue. The result of NextMessage() is checked finally. 119 */ 120 121 void ConcurrencyTest2::TestThread2(void) 122 { 123 snooze(SNOOZE_TIME/10); 124 CPPUNIT_ASSERT(isLocked); 125 if (!unlockTest) { 126 // Be's implementation can cause a segv when NextMessage() is in 127 // progress when a delete occurs. The Haiku implementation 128 // does not segv, but it won't be tested here because Be's fails. 129 return; 130 } 131 BMessage *theMessage = theMessageQueue->NextMessage(); 132 CPPUNIT_ASSERT(!isLocked); 133 134 if (unlockTest) { 135 CPPUNIT_ASSERT(theMessage != NULL); 136 CPPUNIT_ASSERT(theMessage->what == 0); 137 } else { 138 // The following test passes for the Haiku implementation but 139 // fails for the Be implementation. If the BMessageQueue is deleted 140 // while another thread is blocking waiting for NextMessage(), the 141 // Haiku implementation detects that the message queue is deleted 142 // and returns NULL. The Be implementation actually returns a message. 143 // It must be doing so from freed memory since the queue has been 144 // deleted. The Haiku implementation will not emulate the Be 145 // implementation since I consider it a bug. 146 // 147 // CPPUNIT_ASSERT(theMessage==NULL); 148 } 149 } 150 151 152 /* 153 * Method: ConcurrencyTest2::TestThread3() 154 * Descr: This member function is one thread within the test. It 155 * snoozes for a short time so that TestThread1() will grab 156 * the lock. If this is the delete test, this thread 157 * terminates since Be's implementation may corrupt memory. 158 * Otherwise, the thread blocks attempting a RemoveMessage() 159 * on the queue. The state of the queue is checked finally. 160 */ 161 162 void ConcurrencyTest2::TestThread3(void) 163 { 164 snooze(SNOOZE_TIME/10); 165 CPPUNIT_ASSERT(isLocked); 166 if (!unlockTest) { 167 // Be's implementation causes a segv when RemoveMessage() is in 168 // progress when a delete occurs. The Haiku implementation 169 // does not segv, but it won't be tested here because Be's fails. 170 return; 171 } 172 theMessageQueue->RemoveMessage(removeMessage); 173 CPPUNIT_ASSERT(!isLocked); 174 if (unlockTest) { 175 CPPUNIT_ASSERT(theMessageQueue->FindMessage(removeMessage->what, 0) == NULL); 176 } 177 } 178 179 180 /* 181 * Method: ConcurrencyTest2::TestThread1() 182 * Descr: This member function is one thread within the test. It 183 * snoozes for a short time so that TestThread1() will grab 184 * the lock. If this is the delete test, this thread 185 * terminates since Be's implementation may corrupt memory. 186 * Otherwise, the thread blocks attempting a AddMessage() on 187 * the queue. The state of the queue is checked finally. 188 */ 189 190 void ConcurrencyTest2::TestThread4(void) 191 { 192 snooze(SNOOZE_TIME/10); 193 CPPUNIT_ASSERT(isLocked); 194 if (!unlockTest) { 195 // Be's implementation can cause a segv when AddMessage() is in 196 // progress when a delete occurs. The Haiku implementation 197 // does not segv, but it won't be tested here because Be's fails. 198 return; 199 } 200 theMessageQueue->AddMessage(new testMessageClass(numAddMessages)); 201 CPPUNIT_ASSERT(!isLocked); 202 if (unlockTest) { 203 CPPUNIT_ASSERT(theMessageQueue->FindMessage(numAddMessages, 0) != NULL); 204 } 205 } 206 207 208 /* 209 * Method: ConcurrencyTest2::TestThread1() 210 * Descr: This member function is one thread within the test. It 211 * snoozes for a short time so that TestThread1() will grab 212 * the lock. The thread blocks attempting to acquire the 213 * lock on the queue. Once the lock is acquired, mutual 214 * exclusion is checked as well as the result of the lock 215 * acquisition. 216 */ 217 218 void ConcurrencyTest2::TestThread5(void) 219 { 220 SafetyLock mySafetyLock(theMessageQueue); 221 222 snooze(SNOOZE_TIME/10); 223 CPPUNIT_ASSERT(isLocked); 224 bool result = theMessageQueue->Lock(); 225 CPPUNIT_ASSERT(!isLocked); 226 if (unlockTest) { 227 CPPUNIT_ASSERT(result); 228 theMessageQueue->Unlock(); 229 } else { 230 CPPUNIT_ASSERT(!result); 231 } 232 } 233 234 235 /* 236 * Method: ConcurrencyTest2::suite() 237 * Descr: This static member function returns a test suite for performing 238 * all combinations of "ConcurrencyTest2". The test suite contains 239 * two instances of the test. One is performed with an unlock, 240 * the other with a delete. Each individual test 241 * is created as a ThreadedTestCase (typedef'd as 242 * ConcurrencyTest2Caller) with five independent threads. 243 */ 244 245 Test *ConcurrencyTest2::suite(void) 246 { 247 typedef BThreadedTestCaller<ConcurrencyTest2> 248 ConcurrencyTest2Caller; 249 250 TestSuite *testSuite = new TestSuite("ConcurrencyTest2"); 251 252 ConcurrencyTest2 *theTest = new ConcurrencyTest2("WithUnlock", true); 253 ConcurrencyTest2Caller *threadedTest1 = new ConcurrencyTest2Caller("BMessageQueue::Concurrency Test #2 (with unlock)", theTest); 254 threadedTest1->addThread("A", &ConcurrencyTest2::TestThread1); 255 threadedTest1->addThread("B", &ConcurrencyTest2::TestThread2); 256 threadedTest1->addThread("C", &ConcurrencyTest2::TestThread3); 257 threadedTest1->addThread("D", &ConcurrencyTest2::TestThread4); 258 threadedTest1->addThread("E", &ConcurrencyTest2::TestThread5); 259 260 theTest = new ConcurrencyTest2("WithDelete", false); 261 ConcurrencyTest2Caller *threadedTest2 = new ConcurrencyTest2Caller("BMessageQueue::Concurrency Test #2 (with delete)", theTest); 262 threadedTest2->addThread("A", &ConcurrencyTest2::TestThread1); 263 threadedTest2->addThread("B", &ConcurrencyTest2::TestThread2); 264 threadedTest2->addThread("C", &ConcurrencyTest2::TestThread3); 265 threadedTest2->addThread("D", &ConcurrencyTest2::TestThread4); 266 threadedTest2->addThread("E", &ConcurrencyTest2::TestThread5); 267 268 testSuite->addTest(threadedTest1); 269 testSuite->addTest(threadedTest2); 270 return(testSuite); 271 } 272 273 274 275