xref: /haiku/src/tests/kits/app/bmessagequeue/ConcurrencyTest2.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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