xref: /haiku/src/tests/system/kernel/unit/lock/RWLockTests.cpp (revision 4ccd636dcb295bc3860c3c9a06c0ecc4b2fb3fa0)
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 
16 static const int kConcurrentTestTime = 2000000;
17 
18 
19 class RWLockTest : public StandardTestDelegate {
20 public:
RWLockTest()21 	RWLockTest()
22 	{
23 	}
24 
Setup(TestContext & context)25 	virtual status_t Setup(TestContext& context)
26 	{
27 		rw_lock_init(&fLock, "test r/w lock");
28 		return B_OK;
29 	}
30 
Cleanup(TestContext & context,bool setupOK)31 	virtual void Cleanup(TestContext& context, bool setupOK)
32 	{
33 		rw_lock_destroy(&fLock);
34 	}
35 
36 
TestSimple(TestContext & context)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 
TestNestedWrite(TestContext & context)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 
TestNestedWriteRead(TestContext & context)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 
TestDegrade(TestContext & context)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 
TestConcurrentWriteRead(TestContext & context)86 	bool TestConcurrentWriteRead(TestContext& context)
87 	{
88 		return _RunConcurrentTest(context,
89 			&RWLockTest::TestConcurrentWriteReadThread);
90 	}
91 
TestConcurrentWriteNestedRead(TestContext & context)92 	bool TestConcurrentWriteNestedRead(TestContext& context)
93 	{
94 		return _RunConcurrentTest(context,
95 			&RWLockTest::TestConcurrentWriteNestedReadThread);
96 	}
97 
TestConcurrentDegrade(TestContext & context)98 	bool TestConcurrentDegrade(TestContext& context)
99 	{
100 		return _RunConcurrentTest(context,
101 			&RWLockTest::TestConcurrentDegradeThread);
102 	}
103 
104 
105 	// thread function wrappers
106 
TestConcurrentWriteReadThread(TestContext & context,void * _index)107 	void TestConcurrentWriteReadThread(TestContext& context, void* _index)
108 	{
109 		if (!_TestConcurrentWriteReadThread(context, (addr_t)_index))
110 			fTestOK = false;
111 	}
112 
TestConcurrentWriteNestedReadThread(TestContext & context,void * _index)113 	void TestConcurrentWriteNestedReadThread(TestContext& context, void* _index)
114 	{
115 		if (!_TestConcurrentWriteNestedReadThread(context, (addr_t)_index))
116 			fTestOK = false;
117 	}
118 
TestConcurrentDegradeThread(TestContext & context,void * _index)119 	void TestConcurrentDegradeThread(TestContext& context, void* _index)
120 	{
121 		if (!_TestConcurrentDegradeThread(context, (addr_t)_index))
122 			fTestOK = false;
123 	}
124 
125 private:
_RunConcurrentTest(TestContext & context,void (RWLockTest::* method)(TestContext &,void *))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 
_TestConcurrentWriteReadThread(TestContext & context,int32 threadIndex)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 
_TestConcurrentWriteNestedReadThread(TestContext & context,int32 threadIndex)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 
_TestConcurrentDegradeThread(TestContext & context,int32 threadIndex)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 
291 private:
292 			rw_lock		fLock;
293 	volatile bool		fTestGo;
294 	volatile uint64		fLockCount;
295 	volatile bool		fTestOK;
296 };
297 
298 
299 TestSuite*
create_rw_lock_test_suite()300 create_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