1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "RWLockTests.h"
8
9#include <string.h>
10
11#include <lock.h>
12
13#include "TestThread.h"
14
15
16static const int kConcurrentTestTime = 2000000;
17
18
19class RWLockTest : public StandardTestDelegate {
20public:
21	RWLockTest()
22	{
23	}
24
25	virtual status_t Setup(TestContext& context)
26	{
27		rw_lock_init(&fLock, "test r/w lock");
28		return B_OK;
29	}
30
31	virtual void Cleanup(TestContext& context, bool setupOK)
32	{
33		rw_lock_destroy(&fLock);
34	}
35
36
37	bool TestSimple(TestContext& context)
38	{
39		for (int32 i = 0; i < 3; i++) {
40			TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
41			rw_lock_read_unlock(&fLock);
42
43			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
44			rw_lock_write_unlock(&fLock);
45		}
46
47		return true;
48	}
49
50	bool TestNestedWrite(TestContext& context)
51	{
52		for (int32 i = 0; i < 10; i++)
53			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
54
55		for (int32 i = 0; i < 10; i++)
56			rw_lock_write_unlock(&fLock);
57
58		return true;
59	}
60
61	bool TestNestedWriteRead(TestContext& context)
62	{
63		TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
64
65		for (int32 i = 0; i < 10; i++)
66			TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
67
68		for (int32 i = 0; i < 10; i++)
69			rw_lock_read_unlock(&fLock);
70
71		rw_lock_write_unlock(&fLock);
72
73		return true;
74	}
75
76	bool TestDegrade(TestContext& context)
77	{
78		TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
79		TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
80		rw_lock_write_unlock(&fLock);
81		rw_lock_read_unlock(&fLock);
82
83		return true;
84	}
85
86	bool TestConcurrentWriteRead(TestContext& context)
87	{
88		return _RunConcurrentTest(context,
89			&RWLockTest::TestConcurrentWriteReadThread);
90	}
91
92	bool TestConcurrentWriteNestedRead(TestContext& context)
93	{
94		return _RunConcurrentTest(context,
95			&RWLockTest::TestConcurrentWriteNestedReadThread);
96	}
97
98	bool TestConcurrentDegrade(TestContext& context)
99	{
100		return _RunConcurrentTest(context,
101			&RWLockTest::TestConcurrentDegradeThread);
102	}
103
104
105	// thread function wrappers
106
107	void TestConcurrentWriteReadThread(TestContext& context, void* _index)
108	{
109		if (!_TestConcurrentWriteReadThread(context, (addr_t)_index))
110			fTestOK = false;
111	}
112
113	void TestConcurrentWriteNestedReadThread(TestContext& context, void* _index)
114	{
115		if (!_TestConcurrentWriteNestedReadThread(context, (addr_t)_index))
116			fTestOK = false;
117	}
118
119	void TestConcurrentDegradeThread(TestContext& context, void* _index)
120	{
121		if (!_TestConcurrentDegradeThread(context, (addr_t)_index))
122			fTestOK = false;
123	}
124
125private:
126	bool _RunConcurrentTest(TestContext& context,
127		void (RWLockTest::*method)(TestContext&, void*))
128	{
129		const int threadCount = 8;
130		thread_id threads[threadCount];
131		for (int i = 0; i < threadCount; i++)
132			threads[i] = -1;
133
134		fTestOK = true;
135		fTestGo = false;
136		fLockCount = 0;
137
138		for (int i = 0; i < threadCount; i++) {
139			threads[i] = SpawnThread(this, method, "rw lock test",
140				B_NORMAL_PRIORITY, (void*)(addr_t)i);
141			if (threads[i] < 0) {
142				fTestOK = false;
143				context.Error("Failed to spawn thread: %s\n",
144					strerror(threads[i]));
145				break;
146			}
147		}
148
149		for (int i = 0; i < threadCount; i++)
150			resume_thread(threads[i]);
151
152		fTestGo = true;
153
154		for (int i = 0; i < threadCount; i++) {
155			if (threads[i] >= 0)
156				wait_for_thread(threads[i], NULL);
157		}
158
159		return fTestOK;
160	}
161
162	bool _TestConcurrentWriteReadThread(TestContext& context, int32 threadIndex)
163	{
164		if (!fTestOK)
165			return false;
166
167		int bitShift = 8 * threadIndex;
168
169		while (!fTestGo) {
170		}
171
172		bigtime_t startTime = system_time();
173		uint64 iteration = 0;
174		do {
175			for (int k = 0; fTestOK && k < 255; k++) {
176				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
177				uint64 count = fLockCount;
178				rw_lock_read_unlock(&fLock);
179
180				TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
181				fLockCount += (uint64)1 << bitShift;
182				rw_lock_write_unlock(&fLock);
183
184				int value = (count >> bitShift) & 0xff;
185				TEST_ASSERT_PRINT(value == k,
186					"thread index: %" B_PRId32 ", iteration: %" B_PRId32
187					", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
188					iteration, value, k, count);
189			}
190
191			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
192			fLockCount -= (uint64)255 << bitShift;
193			rw_lock_write_unlock(&fLock);
194
195			iteration++;
196		} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
197
198		return true;
199	}
200
201	bool _TestConcurrentWriteNestedReadThread(TestContext& context,
202		int32 threadIndex)
203	{
204		if (!fTestOK)
205			return false;
206
207		int bitShift = 8 * threadIndex;
208
209		while (!fTestGo) {
210		}
211
212		bigtime_t startTime = system_time();
213		uint64 iteration = 0;
214		do {
215			for (int k = 0; fTestOK && k < 255; k++) {
216				TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
217
218				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
219				uint64 count = fLockCount;
220				rw_lock_read_unlock(&fLock);
221
222				fLockCount += (uint64)1 << bitShift;
223
224				rw_lock_write_unlock(&fLock);
225
226				int value = (count >> bitShift) & 0xff;
227				TEST_ASSERT_PRINT(value == k,
228					"thread index: %" B_PRId32 ", iteration: %" B_PRId32
229					", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
230					iteration, value, k, count);
231			}
232
233			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
234			fLockCount -= (uint64)255 << bitShift;
235			rw_lock_write_unlock(&fLock);
236
237			iteration++;
238		} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
239
240		return true;
241	}
242
243	bool _TestConcurrentDegradeThread(TestContext& context, int32 threadIndex)
244	{
245		if (!fTestOK)
246			return false;
247
248		int bitShift = 8 * threadIndex;
249
250		while (!fTestGo) {
251		}
252
253		bigtime_t startTime = system_time();
254		uint64 iteration = 0;
255		do {
256			for (int k = 0; fTestOK && k < 255; k++) {
257				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
258				uint64 count = fLockCount;
259				rw_lock_read_unlock(&fLock);
260
261				TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
262				fLockCount += (uint64)1 << bitShift;
263				uint64 newCount = fLockCount;
264
265				// degrade
266				TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
267				rw_lock_write_unlock(&fLock);
268
269				uint64 unchangedCount = fLockCount;
270
271				rw_lock_read_unlock(&fLock);
272
273				int value = (count >> bitShift) & 0xff;
274				TEST_ASSERT_PRINT(value == k,
275					"thread index: %" B_PRId32 ", iteration: %" B_PRId32
276					", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
277					iteration, value, k, count);
278				TEST_ASSERT(newCount == unchangedCount);
279			}
280
281			TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
282			fLockCount -= (uint64)255 << bitShift;
283			rw_lock_write_unlock(&fLock);
284
285			iteration++;
286		} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
287
288		return true;
289	}
290
291private:
292			rw_lock		fLock;
293	volatile bool		fTestGo;
294	volatile uint64		fLockCount;
295	volatile bool		fTestOK;
296};
297
298
299TestSuite*
300create_rw_lock_test_suite()
301{
302	TestSuite* suite = new(std::nothrow) TestSuite("rw_lock");
303
304	ADD_STANDARD_TEST(suite, RWLockTest, TestSimple);
305	ADD_STANDARD_TEST(suite, RWLockTest, TestNestedWrite);
306	ADD_STANDARD_TEST(suite, RWLockTest, TestNestedWriteRead);
307	ADD_STANDARD_TEST(suite, RWLockTest, TestDegrade);
308	ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentWriteRead);
309	ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentWriteNestedRead);
310	ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentDegrade);
311
312	return suite;
313}
314