xref: /haiku/src/tests/system/kernel/unit/lock/RWLockTests.cpp (revision 4ccd636dcb295bc3860c3c9a06c0ecc4b2fb3fa0)
1933764d7SIngo Weinhold /*
2933764d7SIngo Weinhold  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3933764d7SIngo Weinhold  * Distributed under the terms of the MIT License.
4933764d7SIngo Weinhold  */
5933764d7SIngo Weinhold 
6933764d7SIngo Weinhold 
7933764d7SIngo Weinhold #include "RWLockTests.h"
8933764d7SIngo Weinhold 
9933764d7SIngo Weinhold #include <string.h>
10933764d7SIngo Weinhold 
11933764d7SIngo Weinhold #include <lock.h>
12933764d7SIngo Weinhold 
13933764d7SIngo Weinhold #include "TestThread.h"
14933764d7SIngo Weinhold 
15933764d7SIngo Weinhold 
16933764d7SIngo Weinhold static const int kConcurrentTestTime = 2000000;
17933764d7SIngo Weinhold 
18933764d7SIngo Weinhold 
19933764d7SIngo Weinhold class RWLockTest : public StandardTestDelegate {
20933764d7SIngo Weinhold public:
RWLockTest()21933764d7SIngo Weinhold 	RWLockTest()
22933764d7SIngo Weinhold 	{
23933764d7SIngo Weinhold 	}
24933764d7SIngo Weinhold 
Setup(TestContext & context)25933764d7SIngo Weinhold 	virtual status_t Setup(TestContext& context)
26933764d7SIngo Weinhold 	{
27933764d7SIngo Weinhold 		rw_lock_init(&fLock, "test r/w lock");
28933764d7SIngo Weinhold 		return B_OK;
29933764d7SIngo Weinhold 	}
30933764d7SIngo Weinhold 
Cleanup(TestContext & context,bool setupOK)31933764d7SIngo Weinhold 	virtual void Cleanup(TestContext& context, bool setupOK)
32933764d7SIngo Weinhold 	{
33933764d7SIngo Weinhold 		rw_lock_destroy(&fLock);
34933764d7SIngo Weinhold 	}
35933764d7SIngo Weinhold 
36933764d7SIngo Weinhold 
TestSimple(TestContext & context)37933764d7SIngo Weinhold 	bool TestSimple(TestContext& context)
38933764d7SIngo Weinhold 	{
39933764d7SIngo Weinhold 		for (int32 i = 0; i < 3; i++) {
40933764d7SIngo Weinhold 			TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
41*4ccd636dSIngo Weinhold 			rw_lock_read_unlock(&fLock);
42933764d7SIngo Weinhold 
43933764d7SIngo Weinhold 			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
44*4ccd636dSIngo Weinhold 			rw_lock_write_unlock(&fLock);
45933764d7SIngo Weinhold 		}
46933764d7SIngo Weinhold 
47933764d7SIngo Weinhold 		return true;
48933764d7SIngo Weinhold 	}
49933764d7SIngo Weinhold 
TestNestedWrite(TestContext & context)50933764d7SIngo Weinhold 	bool TestNestedWrite(TestContext& context)
51933764d7SIngo Weinhold 	{
52933764d7SIngo Weinhold 		for (int32 i = 0; i < 10; i++)
53933764d7SIngo Weinhold 			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
54933764d7SIngo Weinhold 
55933764d7SIngo Weinhold 		for (int32 i = 0; i < 10; i++)
56*4ccd636dSIngo Weinhold 			rw_lock_write_unlock(&fLock);
57933764d7SIngo Weinhold 
58933764d7SIngo Weinhold 		return true;
59933764d7SIngo Weinhold 	}
60933764d7SIngo Weinhold 
TestNestedWriteRead(TestContext & context)61933764d7SIngo Weinhold 	bool TestNestedWriteRead(TestContext& context)
62933764d7SIngo Weinhold 	{
63933764d7SIngo Weinhold 		TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
64933764d7SIngo Weinhold 
65933764d7SIngo Weinhold 		for (int32 i = 0; i < 10; i++)
66933764d7SIngo Weinhold 			TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
67933764d7SIngo Weinhold 
68933764d7SIngo Weinhold 		for (int32 i = 0; i < 10; i++)
69*4ccd636dSIngo Weinhold 			rw_lock_read_unlock(&fLock);
70933764d7SIngo Weinhold 
71*4ccd636dSIngo Weinhold 		rw_lock_write_unlock(&fLock);
72*4ccd636dSIngo Weinhold 
73*4ccd636dSIngo Weinhold 		return true;
74*4ccd636dSIngo Weinhold 	}
75*4ccd636dSIngo Weinhold 
TestDegrade(TestContext & context)76*4ccd636dSIngo Weinhold 	bool TestDegrade(TestContext& context)
77*4ccd636dSIngo Weinhold 	{
78*4ccd636dSIngo Weinhold 		TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
79*4ccd636dSIngo Weinhold 		TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
80*4ccd636dSIngo Weinhold 		rw_lock_write_unlock(&fLock);
81*4ccd636dSIngo Weinhold 		rw_lock_read_unlock(&fLock);
82933764d7SIngo Weinhold 
83933764d7SIngo Weinhold 		return true;
84933764d7SIngo Weinhold 	}
85933764d7SIngo Weinhold 
TestConcurrentWriteRead(TestContext & context)86933764d7SIngo Weinhold 	bool TestConcurrentWriteRead(TestContext& context)
87933764d7SIngo Weinhold 	{
88933764d7SIngo Weinhold 		return _RunConcurrentTest(context,
89933764d7SIngo Weinhold 			&RWLockTest::TestConcurrentWriteReadThread);
90933764d7SIngo Weinhold 	}
91933764d7SIngo Weinhold 
TestConcurrentWriteNestedRead(TestContext & context)92933764d7SIngo Weinhold 	bool TestConcurrentWriteNestedRead(TestContext& context)
93933764d7SIngo Weinhold 	{
94933764d7SIngo Weinhold 		return _RunConcurrentTest(context,
95933764d7SIngo Weinhold 			&RWLockTest::TestConcurrentWriteNestedReadThread);
96933764d7SIngo Weinhold 	}
97933764d7SIngo Weinhold 
TestConcurrentDegrade(TestContext & context)98*4ccd636dSIngo Weinhold 	bool TestConcurrentDegrade(TestContext& context)
99*4ccd636dSIngo Weinhold 	{
100*4ccd636dSIngo Weinhold 		return _RunConcurrentTest(context,
101*4ccd636dSIngo Weinhold 			&RWLockTest::TestConcurrentDegradeThread);
102*4ccd636dSIngo Weinhold 	}
103*4ccd636dSIngo Weinhold 
104933764d7SIngo Weinhold 
105933764d7SIngo Weinhold 	// thread function wrappers
106933764d7SIngo Weinhold 
TestConcurrentWriteReadThread(TestContext & context,void * _index)107933764d7SIngo Weinhold 	void TestConcurrentWriteReadThread(TestContext& context, void* _index)
108933764d7SIngo Weinhold 	{
109933764d7SIngo Weinhold 		if (!_TestConcurrentWriteReadThread(context, (addr_t)_index))
110933764d7SIngo Weinhold 			fTestOK = false;
111933764d7SIngo Weinhold 	}
112933764d7SIngo Weinhold 
TestConcurrentWriteNestedReadThread(TestContext & context,void * _index)113933764d7SIngo Weinhold 	void TestConcurrentWriteNestedReadThread(TestContext& context, void* _index)
114933764d7SIngo Weinhold 	{
115933764d7SIngo Weinhold 		if (!_TestConcurrentWriteNestedReadThread(context, (addr_t)_index))
116933764d7SIngo Weinhold 			fTestOK = false;
117933764d7SIngo Weinhold 	}
118933764d7SIngo Weinhold 
TestConcurrentDegradeThread(TestContext & context,void * _index)119*4ccd636dSIngo Weinhold 	void TestConcurrentDegradeThread(TestContext& context, void* _index)
120*4ccd636dSIngo Weinhold 	{
121*4ccd636dSIngo Weinhold 		if (!_TestConcurrentDegradeThread(context, (addr_t)_index))
122*4ccd636dSIngo Weinhold 			fTestOK = false;
123*4ccd636dSIngo Weinhold 	}
124*4ccd636dSIngo Weinhold 
125933764d7SIngo Weinhold private:
_RunConcurrentTest(TestContext & context,void (RWLockTest::* method)(TestContext &,void *))126933764d7SIngo Weinhold 	bool _RunConcurrentTest(TestContext& context,
127933764d7SIngo Weinhold 		void (RWLockTest::*method)(TestContext&, void*))
128933764d7SIngo Weinhold 	{
129933764d7SIngo Weinhold 		const int threadCount = 8;
130933764d7SIngo Weinhold 		thread_id threads[threadCount];
131933764d7SIngo Weinhold 		for (int i = 0; i < threadCount; i++)
132933764d7SIngo Weinhold 			threads[i] = -1;
133933764d7SIngo Weinhold 
134933764d7SIngo Weinhold 		fTestOK = true;
135933764d7SIngo Weinhold 		fTestGo = false;
136933764d7SIngo Weinhold 		fLockCount = 0;
137933764d7SIngo Weinhold 
138933764d7SIngo Weinhold 		for (int i = 0; i < threadCount; i++) {
139933764d7SIngo Weinhold 			threads[i] = SpawnThread(this, method, "rw lock test",
140933764d7SIngo Weinhold 				B_NORMAL_PRIORITY, (void*)(addr_t)i);
141933764d7SIngo Weinhold 			if (threads[i] < 0) {
142933764d7SIngo Weinhold 				fTestOK = false;
143933764d7SIngo Weinhold 				context.Error("Failed to spawn thread: %s\n",
144933764d7SIngo Weinhold 					strerror(threads[i]));
145933764d7SIngo Weinhold 				break;
146933764d7SIngo Weinhold 			}
147933764d7SIngo Weinhold 		}
148933764d7SIngo Weinhold 
149933764d7SIngo Weinhold 		for (int i = 0; i < threadCount; i++)
150933764d7SIngo Weinhold 			resume_thread(threads[i]);
151933764d7SIngo Weinhold 
152933764d7SIngo Weinhold 		fTestGo = true;
153933764d7SIngo Weinhold 
154933764d7SIngo Weinhold 		for (int i = 0; i < threadCount; i++) {
155933764d7SIngo Weinhold 			if (threads[i] >= 0)
156933764d7SIngo Weinhold 				wait_for_thread(threads[i], NULL);
157933764d7SIngo Weinhold 		}
158933764d7SIngo Weinhold 
159933764d7SIngo Weinhold 		return fTestOK;
160933764d7SIngo Weinhold 	}
161933764d7SIngo Weinhold 
_TestConcurrentWriteReadThread(TestContext & context,int32 threadIndex)162933764d7SIngo Weinhold 	bool _TestConcurrentWriteReadThread(TestContext& context, int32 threadIndex)
163933764d7SIngo Weinhold 	{
164933764d7SIngo Weinhold 		if (!fTestOK)
165933764d7SIngo Weinhold 			return false;
166933764d7SIngo Weinhold 
167933764d7SIngo Weinhold 		int bitShift = 8 * threadIndex;
168933764d7SIngo Weinhold 
169933764d7SIngo Weinhold 		while (!fTestGo) {
170933764d7SIngo Weinhold 		}
171933764d7SIngo Weinhold 
172933764d7SIngo Weinhold 		bigtime_t startTime = system_time();
173933764d7SIngo Weinhold 		uint64 iteration = 0;
174933764d7SIngo Weinhold 		do {
175933764d7SIngo Weinhold 			for (int k = 0; fTestOK && k < 255; k++) {
176933764d7SIngo Weinhold 				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
177933764d7SIngo Weinhold 				uint64 count = fLockCount;
178*4ccd636dSIngo Weinhold 				rw_lock_read_unlock(&fLock);
179933764d7SIngo Weinhold 
180933764d7SIngo Weinhold 				TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
181933764d7SIngo Weinhold 				fLockCount += (uint64)1 << bitShift;
182*4ccd636dSIngo Weinhold 				rw_lock_write_unlock(&fLock);
183933764d7SIngo Weinhold 
184933764d7SIngo Weinhold 				int value = (count >> bitShift) & 0xff;
185933764d7SIngo Weinhold 				TEST_ASSERT_PRINT(value == k,
186933764d7SIngo Weinhold 					"thread index: %" B_PRId32 ", iteration: %" B_PRId32
187933764d7SIngo Weinhold 					", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
188933764d7SIngo Weinhold 					iteration, value, k, count);
189933764d7SIngo Weinhold 			}
190933764d7SIngo Weinhold 
191933764d7SIngo Weinhold 			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
192933764d7SIngo Weinhold 			fLockCount -= (uint64)255 << bitShift;
193*4ccd636dSIngo Weinhold 			rw_lock_write_unlock(&fLock);
194933764d7SIngo Weinhold 
195933764d7SIngo Weinhold 			iteration++;
196933764d7SIngo Weinhold 		} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
197933764d7SIngo Weinhold 
198933764d7SIngo Weinhold 		return true;
199933764d7SIngo Weinhold 	}
200933764d7SIngo Weinhold 
_TestConcurrentWriteNestedReadThread(TestContext & context,int32 threadIndex)201933764d7SIngo Weinhold 	bool _TestConcurrentWriteNestedReadThread(TestContext& context,
202933764d7SIngo Weinhold 		int32 threadIndex)
203933764d7SIngo Weinhold 	{
204933764d7SIngo Weinhold 		if (!fTestOK)
205933764d7SIngo Weinhold 			return false;
206933764d7SIngo Weinhold 
207933764d7SIngo Weinhold 		int bitShift = 8 * threadIndex;
208933764d7SIngo Weinhold 
209933764d7SIngo Weinhold 		while (!fTestGo) {
210933764d7SIngo Weinhold 		}
211933764d7SIngo Weinhold 
212933764d7SIngo Weinhold 		bigtime_t startTime = system_time();
213933764d7SIngo Weinhold 		uint64 iteration = 0;
214933764d7SIngo Weinhold 		do {
215933764d7SIngo Weinhold 			for (int k = 0; fTestOK && k < 255; k++) {
216933764d7SIngo Weinhold 				TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
217933764d7SIngo Weinhold 
218933764d7SIngo Weinhold 				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
219933764d7SIngo Weinhold 				uint64 count = fLockCount;
220*4ccd636dSIngo Weinhold 				rw_lock_read_unlock(&fLock);
221933764d7SIngo Weinhold 
222933764d7SIngo Weinhold 				fLockCount += (uint64)1 << bitShift;
223933764d7SIngo Weinhold 
224*4ccd636dSIngo Weinhold 				rw_lock_write_unlock(&fLock);
225933764d7SIngo Weinhold 
226933764d7SIngo Weinhold 				int value = (count >> bitShift) & 0xff;
227933764d7SIngo Weinhold 				TEST_ASSERT_PRINT(value == k,
228933764d7SIngo Weinhold 					"thread index: %" B_PRId32 ", iteration: %" B_PRId32
229933764d7SIngo Weinhold 					", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
230933764d7SIngo Weinhold 					iteration, value, k, count);
231933764d7SIngo Weinhold 			}
232933764d7SIngo Weinhold 
233933764d7SIngo Weinhold 			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
234933764d7SIngo Weinhold 			fLockCount -= (uint64)255 << bitShift;
235*4ccd636dSIngo Weinhold 			rw_lock_write_unlock(&fLock);
236*4ccd636dSIngo Weinhold 
237*4ccd636dSIngo Weinhold 			iteration++;
238*4ccd636dSIngo Weinhold 		} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
239*4ccd636dSIngo Weinhold 
240*4ccd636dSIngo Weinhold 		return true;
241*4ccd636dSIngo Weinhold 	}
242*4ccd636dSIngo Weinhold 
_TestConcurrentDegradeThread(TestContext & context,int32 threadIndex)243*4ccd636dSIngo Weinhold 	bool _TestConcurrentDegradeThread(TestContext& context, int32 threadIndex)
244*4ccd636dSIngo Weinhold 	{
245*4ccd636dSIngo Weinhold 		if (!fTestOK)
246*4ccd636dSIngo Weinhold 			return false;
247*4ccd636dSIngo Weinhold 
248*4ccd636dSIngo Weinhold 		int bitShift = 8 * threadIndex;
249*4ccd636dSIngo Weinhold 
250*4ccd636dSIngo Weinhold 		while (!fTestGo) {
251*4ccd636dSIngo Weinhold 		}
252*4ccd636dSIngo Weinhold 
253*4ccd636dSIngo Weinhold 		bigtime_t startTime = system_time();
254*4ccd636dSIngo Weinhold 		uint64 iteration = 0;
255*4ccd636dSIngo Weinhold 		do {
256*4ccd636dSIngo Weinhold 			for (int k = 0; fTestOK && k < 255; k++) {
257*4ccd636dSIngo Weinhold 				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
258*4ccd636dSIngo Weinhold 				uint64 count = fLockCount;
259*4ccd636dSIngo Weinhold 				rw_lock_read_unlock(&fLock);
260*4ccd636dSIngo Weinhold 
261*4ccd636dSIngo Weinhold 				TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
262*4ccd636dSIngo Weinhold 				fLockCount += (uint64)1 << bitShift;
263*4ccd636dSIngo Weinhold 				uint64 newCount = fLockCount;
264*4ccd636dSIngo Weinhold 
265*4ccd636dSIngo Weinhold 				// degrade
266*4ccd636dSIngo Weinhold 				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
267*4ccd636dSIngo Weinhold 				rw_lock_write_unlock(&fLock);
268*4ccd636dSIngo Weinhold 
269*4ccd636dSIngo Weinhold 				uint64 unchangedCount = fLockCount;
270*4ccd636dSIngo Weinhold 
271*4ccd636dSIngo Weinhold 				rw_lock_read_unlock(&fLock);
272*4ccd636dSIngo Weinhold 
273*4ccd636dSIngo Weinhold 				int value = (count >> bitShift) & 0xff;
274*4ccd636dSIngo Weinhold 				TEST_ASSERT_PRINT(value == k,
275*4ccd636dSIngo Weinhold 					"thread index: %" B_PRId32 ", iteration: %" B_PRId32
276*4ccd636dSIngo Weinhold 					", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
277*4ccd636dSIngo Weinhold 					iteration, value, k, count);
278*4ccd636dSIngo Weinhold 				TEST_ASSERT(newCount == unchangedCount);
279*4ccd636dSIngo Weinhold 			}
280*4ccd636dSIngo Weinhold 
281*4ccd636dSIngo Weinhold 			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
282*4ccd636dSIngo Weinhold 			fLockCount -= (uint64)255 << bitShift;
283*4ccd636dSIngo Weinhold 			rw_lock_write_unlock(&fLock);
284933764d7SIngo Weinhold 
285933764d7SIngo Weinhold 			iteration++;
286933764d7SIngo Weinhold 		} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
287933764d7SIngo Weinhold 
288933764d7SIngo Weinhold 		return true;
289933764d7SIngo Weinhold 	}
290933764d7SIngo Weinhold 
291933764d7SIngo Weinhold private:
292933764d7SIngo Weinhold 			rw_lock		fLock;
293933764d7SIngo Weinhold 	volatile bool		fTestGo;
294933764d7SIngo Weinhold 	volatile uint64		fLockCount;
295933764d7SIngo Weinhold 	volatile bool		fTestOK;
296933764d7SIngo Weinhold };
297933764d7SIngo Weinhold 
298933764d7SIngo Weinhold 
299933764d7SIngo Weinhold TestSuite*
create_rw_lock_test_suite()300933764d7SIngo Weinhold create_rw_lock_test_suite()
301933764d7SIngo Weinhold {
302933764d7SIngo Weinhold 	TestSuite* suite = new(std::nothrow) TestSuite("rw_lock");
303933764d7SIngo Weinhold 
304933764d7SIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestSimple);
305933764d7SIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestNestedWrite);
306933764d7SIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestNestedWriteRead);
307*4ccd636dSIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestDegrade);
308933764d7SIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentWriteRead);
309933764d7SIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentWriteNestedRead);
310*4ccd636dSIngo Weinhold 	ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentDegrade);
311933764d7SIngo Weinhold 
312933764d7SIngo Weinhold 	return suite;
313933764d7SIngo Weinhold }
314