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.
31const int numAddMessages = 50;
32
33// This constant is used as a base amount of time to snooze.
34bigtime_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