janwas
e0dfbe719d
(these are the old self-tests ripped out as well as new ones) note: have not run yet; several would surely fail if they run (can't anyway until cxxtest is installed) This was SVN commit r3912.
212 lines
5.2 KiB
C++
212 lines
5.2 KiB
C++
#include <cxxtest/TestSuite.h>
|
|
|
|
#include "lib/self_test.h"
|
|
#include "lib/posix.h"
|
|
#include "lib/lockfree.h"
|
|
|
|
// make sure the data structures work at all; doesn't test thread-safety.
|
|
class TestLockfreeBasic : public CxxTest::TestSuite
|
|
{
|
|
public:
|
|
void test_basic_single_threaded()
|
|
{
|
|
void* user_data;
|
|
|
|
const uint ENTRIES = 50;
|
|
// should be more than max # retired nodes to test release..() code
|
|
uintptr_t key = 0x1000;
|
|
uint sig = 10;
|
|
|
|
LFList list;
|
|
TS_ASSERT_OK(lfl_init(&list));
|
|
|
|
LFHash hash;
|
|
TS_ASSERT_OK(lfh_init(&hash, 8));
|
|
|
|
// add some entries; store "signatures" (ascending int values)
|
|
for(uint i = 0; i < ENTRIES; i++)
|
|
{
|
|
int was_inserted;
|
|
|
|
user_data = lfl_insert(&list, key+i, sizeof(int), &was_inserted);
|
|
TS_ASSERT(user_data != 0 && was_inserted);
|
|
*(uint*)user_data = sig+i;
|
|
|
|
user_data = lfh_insert(&hash, key+i, sizeof(int), &was_inserted);
|
|
TS_ASSERT(user_data != 0 && was_inserted);
|
|
*(uint*)user_data = sig+i;
|
|
}
|
|
|
|
// make sure all "signatures" are present in list
|
|
for(uint i = 0; i < ENTRIES; i++)
|
|
{
|
|
user_data = lfl_find(&list, key+i);
|
|
TS_ASSERT(user_data != 0);
|
|
TS_ASSERT_EQUAL(*(uint*)user_data, sig+i);
|
|
|
|
user_data = lfh_find(&hash, key+i);
|
|
TS_ASSERT(user_data != 0);
|
|
TS_ASSERT_EQUAL(*(uint*)user_data, sig+i);
|
|
}
|
|
|
|
lfl_free(&list);
|
|
lfh_free(&hash);
|
|
}
|
|
};
|
|
|
|
|
|
// known to fail on P4 due to mem reordering and lack of membars.
|
|
class TestMultithread : public CxxTest::TestSuite
|
|
{
|
|
// poor man's synchronization "barrier"
|
|
bool is_complete;
|
|
intptr_t num_active_threads;
|
|
|
|
LFList list;
|
|
LFHash hash;
|
|
|
|
typedef std::set<uintptr_t> KeySet;
|
|
typedef KeySet::const_iterator KeySetIt;
|
|
KeySet keys;
|
|
pthread_mutex_t mutex; // protects <keys>
|
|
|
|
static void* thread_func(void* arg)
|
|
{
|
|
debug_set_thread_name("LF_test");
|
|
|
|
const uintptr_t thread_number = (uintptr_t)arg;
|
|
|
|
atomic_add(&num_active_threads, 1);
|
|
|
|
// chosen randomly every iteration (int_value % 4)
|
|
enum TestAction
|
|
{
|
|
TA_FIND = 0,
|
|
TA_INSERT = 1,
|
|
TA_ERASE = 2,
|
|
TA_SLEEP = 3
|
|
};
|
|
static const char* const action_strings[] =
|
|
{
|
|
"find", "insert", "erase", "sleep"
|
|
};
|
|
|
|
while(!is_complete)
|
|
{
|
|
void* user_data;
|
|
|
|
const int action = rand(0, 4);
|
|
const uintptr_t key = rand(0, 100);
|
|
const int sleep_duration_ms = rand(0, 100);
|
|
debug_printf("thread %d: %s\n", thread_number, action_strings[action]);
|
|
|
|
//
|
|
pthread_mutex_lock(&mutex);
|
|
const bool was_in_set = keys.find(key) != keys.end();
|
|
if(action == TA_INSERT)
|
|
keys.insert(key);
|
|
else if(action == TA_ERASE)
|
|
keys.erase(key);
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
switch(action)
|
|
{
|
|
case TA_FIND:
|
|
{
|
|
user_data = lfl_find(&list, key);
|
|
TS_ASSERT(was_in_set == (user_data != 0));
|
|
if(user_data)
|
|
TS_ASSERT_EQUAL(*(uintptr_t*)user_data, ~key);
|
|
|
|
user_data = lfh_find(&hash, key);
|
|
// typical failure site if lockfree data structure has bugs.
|
|
TS_ASSERT(was_in_set == (user_data != 0));
|
|
if(user_data)
|
|
TS_ASSERT_EQUAL(*(uintptr_t*)user_data, ~key);
|
|
}
|
|
break;
|
|
|
|
case TA_INSERT:
|
|
{
|
|
int was_inserted;
|
|
|
|
user_data = lfl_insert(&list, key, sizeof(uintptr_t), &was_inserted);
|
|
TS_ASSERT(user_data != 0); // only triggers if out of memory
|
|
*(uintptr_t*)user_data = ~key; // checked above
|
|
TS_ASSERT(was_in_set == !was_inserted);
|
|
|
|
user_data = lfh_insert(&hash, key, sizeof(uintptr_t), &was_inserted);
|
|
TS_ASSERT(user_data != 0); // only triggers if out of memory
|
|
*(uintptr_t*)user_data = ~key; // checked above
|
|
TS_ASSERT(was_in_set == !was_inserted);
|
|
}
|
|
break;
|
|
|
|
case TA_ERASE:
|
|
{
|
|
int err;
|
|
|
|
err = lfl_erase(&list, key);
|
|
TS_ASSERT(was_in_set == (err == ERR_OK));
|
|
|
|
err = lfh_erase(&hash, key);
|
|
TS_ASSERT(was_in_set == (err == ERR_OK));
|
|
}
|
|
break;
|
|
|
|
case TA_SLEEP:
|
|
usleep(sleep_duration_ms*1000);
|
|
break;
|
|
|
|
default:
|
|
TS_FAIL(L"invalid TA_* action");
|
|
break;
|
|
} // switch
|
|
} // while !is_complete
|
|
|
|
atomic_add(&num_active_threads, -1);
|
|
TS_ASSERT(num_active_threads >= 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public:
|
|
TestMultithread()
|
|
: is_complete(false), num_active_threads(0),
|
|
list(), hash(),
|
|
mutex(0) {}
|
|
|
|
void test_multithread()
|
|
{
|
|
// this test is randomized; we need deterministic results.
|
|
srand(1);
|
|
|
|
static const double TEST_LENGTH = 30.; // [seconds]
|
|
const double end_time = get_time() + TEST_LENGTH;
|
|
is_complete = false;
|
|
|
|
TS_ASSERT_OK(lfl_init(&list));
|
|
TS_ASSERT_OK(lfh_init(&hash, 128));
|
|
TS_ASSERT_OK(pthread_mutex_init(&mutex, 0));
|
|
|
|
// spin off test threads (many, to force preemption)
|
|
const uint NUM_THREADS = 16;
|
|
for(uintptr_t i = 0; i < NUM_THREADS; i++)
|
|
pthread_create(0, 0, thread_func, (void*)i);
|
|
|
|
// wait until time interval elapsed (if we get that far, all is well).
|
|
while(get_time() < end_time)
|
|
usleep(10*1000);
|
|
|
|
// signal and wait for all threads to complete (poor man's barrier -
|
|
// those aren't currently implemented in wpthread).
|
|
is_complete = true;
|
|
while(num_active_threads > 0)
|
|
usleep(5*1000);
|
|
|
|
lfl_free(&list);
|
|
lfh_free(&hash);
|
|
TS_ASSERT_OK(pthread_mutex_destroy(&mutex));
|
|
}
|
|
};
|