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: 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 125 private: 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 291 private: 292 rw_lock fLock; 293 volatile bool fTestGo; 294 volatile uint64 fLockCount; 295 volatile bool fTestOK; 296 }; 297 298 299 TestSuite* 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