1/*
2	$Id: ConcurrencyTest2.cpp 301 2002-07-18 05:32:00Z tylerdauwalder $
3
4	This file implements a test class for testing BLocker functionality.
5	It tests use cases "Locking 1", "Locking 2", "Unlocking", "Is Locked",
6	"Locking Thread" and "Count Locks".  It is essentially the same as Test1.cpp
7	except it makes the first LockWithTimeout inside the threads timeout.  The
8	reason for this is because the implementation of BLocker by Be and with Haiku
9	is such that after one timeout occurs on a benaphore style BLocker, the lock
10	effectively becomes a semaphore style BLocker.  This test tests that condition.
11
12	*/
13
14
15#include "ThreadedTestCaller.h"
16#include "ConcurrencyTest2.h"
17#include "cppunit/TestSuite.h"
18#include "Locker.h"
19
20
21// This constant indicates the number of times the thread should test the
22// acquisition and release of the BLocker.
23
24const int32 MAXLOOP = 10000;
25
26// This constant is used to determine the number of microseconds to
27// sleep during major steps of the test.
28
29const bigtime_t SNOOZE_TIME = 200000;
30
31/*
32 *  Method:  ConcurrencyTest2::ConcurrencyTest2()
33 *   Descr:  This is the only constructor for this test case.  It takes a
34 *           test name and a flag to indicate whether to test a benaphore
35 *           or semaphore type BLocker.
36 */
37
38
39	ConcurrencyTest2::ConcurrencyTest2(std::string name, bool benaphoreFlag) :
40	LockerTestCase(name, benaphoreFlag), lockTestValue(false)
41{
42	}
43
44
45/*
46 *  Method:  ConcurrencyTest2::~ConcurrencyTest2()
47 *   Descr:  This is the descriptor for this test case.
48 */
49
50
51	ConcurrencyTest2::~ConcurrencyTest2()
52{
53	}
54
55
56/*
57 *  Method:  ConcurrencyTest2::setUp()
58 *   Descr:  This member is called before starting the actual test threads
59 *           and is used to ensure that the class is initialized for the
60 *           testing.  It just sets the "lockTestValue" flag to false.  This
61 *           flag is used to show that there is mutual exclusion between the
62 *           threads.
63 */
64
65void
66	ConcurrencyTest2::setUp(void)
67{
68	lockTestValue = false;
69	}
70
71
72/*
73 *  Method:  ConcurrencyTest2::suite()
74 *   Descr:  This static member function returns a test suite for performing
75 *           all combinations of "ConcurrencyTest2".  The test suite contains
76 *           two instances of the test.  One is performed on a benaphore,
77 *           the other on a semaphore based BLocker.  Each individual test
78 *           is created as a ThreadedTestCase (typedef'd as
79 *           ConcurrencyTest2Caller) with three independent threads.
80 */
81
82CppUnit::Test *ConcurrencyTest2::suite(void)
83{
84	typedef BThreadedTestCaller <ConcurrencyTest2 >
85		ConcurrencyTest2Caller;
86	CppUnit::TestSuite *testSuite = new CppUnit::TestSuite("ConcurrencyTest2");
87
88	// Make a benaphore based test object, create a ThreadedTestCase for it and add
89	// three threads to it.
90	ConcurrencyTest2 *theTest = new ConcurrencyTest2("Benaphore", true);
91	ConcurrencyTest2Caller *threadedTest1 = new ConcurrencyTest2Caller("BLocker::Concurrency Test #2 (benaphore)", theTest);
92	threadedTest1->addThread("Acquire", &ConcurrencyTest2::AcquireThread);
93	threadedTest1->addThread("Timeout1", &ConcurrencyTest2::TimeoutThread);
94	threadedTest1->addThread("Timeout2", &ConcurrencyTest2::TimeoutThread);
95
96	// Make a semaphore based test object, create a ThreadedTestCase for it and add
97	// three threads to it.
98	theTest = new ConcurrencyTest2("Semaphore", false);
99	ConcurrencyTest2Caller *threadedTest2 = new ConcurrencyTest2Caller("BLocker::Concurrency Test #2 (semaphore)", theTest);
100	threadedTest2->addThread("Acquire", &ConcurrencyTest2::AcquireThread);
101	threadedTest2->addThread("Timeout1", &ConcurrencyTest2::TimeoutThread);
102	threadedTest2->addThread("Timeout2", &ConcurrencyTest2::TimeoutThread);
103
104	testSuite->addTest(threadedTest1);
105	testSuite->addTest(threadedTest2);
106	return(testSuite);
107	}
108
109
110/*
111 *  Method:  ConcurrencyTest2::AcquireThread()
112 *   Descr:  This member function acquires the lock, sleeps for SNOOZE_TIME,
113 *           releases the lock and then launches into the lock loop test.
114 */
115
116void ConcurrencyTest2::AcquireThread(void)
117{
118	SafetyLock theSafetyLock(theLocker);
119
120	CPPUNIT_ASSERT(theLocker->Lock());
121	NextSubTest();
122	snooze(SNOOZE_TIME);
123	NextSubTest();
124	theLocker->Unlock();
125	NextSubTest();
126	LockingLoop();
127	NextSubTest();
128	}
129
130
131/*
132 *  Method:  ConcurrencyTest2::AcquireLock()
133 *   Descr:  This member function is passed the number of times through the
134 *           acquisition loop (lockAttempt) and whether or not this is
135 *           the first acquisition of the lock within this iteration.
136 *           Based on these values, it may do a LockWithTimeout() or just
137 *           a plain Lock() on theLocker.  This is done to get coverage of
138 *           both lock acquisition methods on the BLocker.
139 */
140
141bool ConcurrencyTest2::AcquireLock(int lockAttempt,
142                                                       bool firstAcquisition)
143{
144	bool timeoutLock;
145	bool result;
146
147	if (firstAcquisition) {
148		timeoutLock = ((lockAttempt % 2) == 1);
149	} else {
150		timeoutLock = (((lockAttempt / 2) % 2) == 1);
151	}
152	if (timeoutLock) {
153		result = (theLocker->LockWithTimeout(1000000) == B_OK);
154	} else {
155		result = theLocker->Lock();
156	}
157	return(result);
158}
159
160
161/*
162 *  Method:  ConcurrencyTest2::TimeoutThread()
163 *   Descr:  This member function sleeps for a short time and then attempts to
164 *           acquire the lock for SNOOZE_TIME/10 seconds.  This acquisition
165 *           should timeout.  Then the locking loop is started.
166 */
167
168void ConcurrencyTest2::TimeoutThread(void)
169{
170	SafetyLock theSafetyLock(theLocker);
171
172	snooze(SNOOZE_TIME/2);
173	NextSubTest();
174	CPPUNIT_ASSERT(theLocker->LockWithTimeout(SNOOZE_TIME/10) == B_TIMED_OUT);
175	NextSubTest();
176	LockingLoop();
177	NextSubTest();
178}
179
180
181/*
182 *  Method:  ConcurrencyTest2::TestThread()
183 *   Descr:  This method is the core of the test.  Each of the three threads
184 *           run this method to perform the concurrency test.  First, the
185 *           SafetyLock class (see LockerTestCase.h) is used to make sure that
186 *           the lock is released if an assertion happens.  Then, each thread
187 *           iterates MAXLOOP times through the main loop where the following
188 *           actions are performed:
189 *              - CheckLock() is used to show that the thread does not have
190 *                the lock.
191 *              - The thread acquires the lock.
192 *              - The thread confirms that mutual exclusion is OK by testing
193 *                lockTestValue.
194 *              - The thread confirms the lock is held once by the thread.
195 *              - The thread acquires the lock again.
196 *              - The thread confirms the lock is held twice now by the thread.
197 *              - The thread releases the lock once.
198 *              - The thread confirms the lock is held once now.
199 *              - The thread confirms that mutual exclusion is still OK by
200 *                testing lockTestValue.
201 *              - The thread releases the lock again.
202 *              - The thread confirms that the lock is no longer held.
203 */
204
205void ConcurrencyTest2::LockingLoop(void)
206{
207	int i;
208	SafetyLock theSafetyLock(theLocker);
209
210	for (i = 0; i < MAXLOOP; i++) {
211		CheckLock(0);
212		CPPUNIT_ASSERT(AcquireLock(i, true));
213
214		CPPUNIT_ASSERT(!lockTestValue);
215		lockTestValue = true;
216		CheckLock(1);
217
218		CPPUNIT_ASSERT(AcquireLock(i, false));
219		CheckLock(2);
220
221		theLocker->Unlock();
222		CheckLock(1);
223
224		CPPUNIT_ASSERT(lockTestValue);
225		lockTestValue = false;
226		theLocker->Unlock();
227		CheckLock(0);
228	}
229}
230
231
232
233