* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "RWLockTests.h"
#include <string.h>
#include <lock.h>
#include "TestThread.h"
static const int kConcurrentTestTime = 2000000;
class RWLockTest : public StandardTestDelegate {
public:
RWLockTest()
{
}
virtual status_t Setup(TestContext& context)
{
rw_lock_init(&fLock, "test r/w lock");
return B_OK;
}
virtual void Cleanup(TestContext& context, bool setupOK)
{
rw_lock_destroy(&fLock);
}
bool TestSimple(TestContext& context)
{
for (int32 i = 0; i < 3; i++) {
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
rw_lock_read_unlock(&fLock);
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
rw_lock_write_unlock(&fLock);
}
return true;
}
bool TestNestedWrite(TestContext& context)
{
for (int32 i = 0; i < 10; i++)
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
for (int32 i = 0; i < 10; i++)
rw_lock_write_unlock(&fLock);
return true;
}
bool TestNestedWriteRead(TestContext& context)
{
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
for (int32 i = 0; i < 10; i++)
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
for (int32 i = 0; i < 10; i++)
rw_lock_read_unlock(&fLock);
rw_lock_write_unlock(&fLock);
return true;
}
bool TestDegrade(TestContext& context)
{
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
rw_lock_write_unlock(&fLock);
rw_lock_read_unlock(&fLock);
return true;
}
bool TestConcurrentWriteRead(TestContext& context)
{
return _RunConcurrentTest(context,
&RWLockTest::TestConcurrentWriteReadThread);
}
bool TestConcurrentWriteNestedRead(TestContext& context)
{
return _RunConcurrentTest(context,
&RWLockTest::TestConcurrentWriteNestedReadThread);
}
bool TestConcurrentDegrade(TestContext& context)
{
return _RunConcurrentTest(context,
&RWLockTest::TestConcurrentDegradeThread);
}
void TestConcurrentWriteReadThread(TestContext& context, void* _index)
{
if (!_TestConcurrentWriteReadThread(context, (addr_t)_index))
fTestOK = false;
}
void TestConcurrentWriteNestedReadThread(TestContext& context, void* _index)
{
if (!_TestConcurrentWriteNestedReadThread(context, (addr_t)_index))
fTestOK = false;
}
void TestConcurrentDegradeThread(TestContext& context, void* _index)
{
if (!_TestConcurrentDegradeThread(context, (addr_t)_index))
fTestOK = false;
}
private:
bool _RunConcurrentTest(TestContext& context,
void (RWLockTest::*method)(TestContext&, void*))
{
const int threadCount = 8;
thread_id threads[threadCount];
for (int i = 0; i < threadCount; i++)
threads[i] = -1;
fTestOK = true;
fTestGo = false;
fLockCount = 0;
for (int i = 0; i < threadCount; i++) {
threads[i] = SpawnThread(this, method, "rw lock test",
B_NORMAL_PRIORITY, (void*)(addr_t)i);
if (threads[i] < 0) {
fTestOK = false;
context.Error("Failed to spawn thread: %s\n",
strerror(threads[i]));
break;
}
}
for (int i = 0; i < threadCount; i++)
resume_thread(threads[i]);
fTestGo = true;
for (int i = 0; i < threadCount; i++) {
if (threads[i] >= 0)
wait_for_thread(threads[i], NULL);
}
return fTestOK;
}
bool _TestConcurrentWriteReadThread(TestContext& context, int32 threadIndex)
{
if (!fTestOK)
return false;
int bitShift = 8 * threadIndex;
while (!fTestGo) {
}
bigtime_t startTime = system_time();
uint64 iteration = 0;
do {
for (int k = 0; fTestOK && k < 255; k++) {
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
uint64 count = fLockCount;
rw_lock_read_unlock(&fLock);
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
fLockCount += (uint64)1 << bitShift;
rw_lock_write_unlock(&fLock);
int value = (count >> bitShift) & 0xff;
TEST_ASSERT_PRINT(value == k,
"thread index: %" B_PRId32 ", iteration: %" B_PRId32
", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
iteration, value, k, count);
}
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
fLockCount -= (uint64)255 << bitShift;
rw_lock_write_unlock(&fLock);
iteration++;
} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
return true;
}
bool _TestConcurrentWriteNestedReadThread(TestContext& context,
int32 threadIndex)
{
if (!fTestOK)
return false;
int bitShift = 8 * threadIndex;
while (!fTestGo) {
}
bigtime_t startTime = system_time();
uint64 iteration = 0;
do {
for (int k = 0; fTestOK && k < 255; k++) {
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
uint64 count = fLockCount;
rw_lock_read_unlock(&fLock);
fLockCount += (uint64)1 << bitShift;
rw_lock_write_unlock(&fLock);
int value = (count >> bitShift) & 0xff;
TEST_ASSERT_PRINT(value == k,
"thread index: %" B_PRId32 ", iteration: %" B_PRId32
", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
iteration, value, k, count);
}
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
fLockCount -= (uint64)255 << bitShift;
rw_lock_write_unlock(&fLock);
iteration++;
} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
return true;
}
bool _TestConcurrentDegradeThread(TestContext& context, int32 threadIndex)
{
if (!fTestOK)
return false;
int bitShift = 8 * threadIndex;
while (!fTestGo) {
}
bigtime_t startTime = system_time();
uint64 iteration = 0;
do {
for (int k = 0; fTestOK && k < 255; k++) {
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
uint64 count = fLockCount;
rw_lock_read_unlock(&fLock);
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
fLockCount += (uint64)1 << bitShift;
uint64 newCount = fLockCount;
TEST_ASSERT(rw_lock_read_lock(&fLock) == B_OK);
rw_lock_write_unlock(&fLock);
uint64 unchangedCount = fLockCount;
rw_lock_read_unlock(&fLock);
int value = (count >> bitShift) & 0xff;
TEST_ASSERT_PRINT(value == k,
"thread index: %" B_PRId32 ", iteration: %" B_PRId32
", value: %d vs %d, count: %#" B_PRIx64, threadIndex,
iteration, value, k, count);
TEST_ASSERT(newCount == unchangedCount);
}
TEST_ASSERT(rw_lock_write_lock(&fLock) == B_OK);
fLockCount -= (uint64)255 << bitShift;
rw_lock_write_unlock(&fLock);
iteration++;
} while (fTestOK && system_time() - startTime < kConcurrentTestTime);
return true;
}
private:
rw_lock fLock;
volatile bool fTestGo;
volatile uint64 fLockCount;
volatile bool fTestOK;
};
TestSuite*
create_rw_lock_test_suite()
{
TestSuite* suite = new(std::nothrow) TestSuite("rw_lock");
ADD_STANDARD_TEST(suite, RWLockTest, TestSimple);
ADD_STANDARD_TEST(suite, RWLockTest, TestNestedWrite);
ADD_STANDARD_TEST(suite, RWLockTest, TestNestedWriteRead);
ADD_STANDARD_TEST(suite, RWLockTest, TestDegrade);
ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentWriteRead);
ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentWriteNestedRead);
ADD_STANDARD_TEST(suite, RWLockTest, TestConcurrentDegrade);
return suite;
}