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