582 lines
16 KiB
C++
582 lines
16 KiB
C++
#include "stdafx.h"
|
|
|
|
//#define RL_MSVC_OUTPUT
|
|
|
|
#include "../relacy/relacy_std.hpp"
|
|
#include "memory_order.hpp"
|
|
#include "fence.hpp"
|
|
#include "data_race.hpp"
|
|
#include "mutex.hpp"
|
|
#include "condvar.hpp"
|
|
#include "semaphore.hpp"
|
|
#include "event.hpp"
|
|
#include "scheduler.hpp"
|
|
#include "compare_swap.hpp"
|
|
#include "wfmo.hpp"
|
|
#include "thread_local.hpp"
|
|
#include "dyn_thread.hpp"
|
|
#include "memory.hpp"
|
|
#include "pthread.hpp"
|
|
#include "windows.hpp"
|
|
#include "addr_hash.hpp"
|
|
#include "futex.hpp"
|
|
|
|
#include "../relacy/windows.h"
|
|
#include "../relacy/pthread.h"
|
|
|
|
#include <cstdio>
|
|
#include <climits>
|
|
|
|
class queue_t
|
|
{
|
|
public:
|
|
queue_t()
|
|
{
|
|
VAR(head) = 0;
|
|
VAR(tail) = 0;
|
|
pthread_mutex_init(&mtx, 0);
|
|
pthread_cond_init(&cv, 0);
|
|
}
|
|
|
|
~queue_t()
|
|
{
|
|
pthread_mutex_destroy(&mtx);
|
|
pthread_cond_destroy(&cv);
|
|
}
|
|
|
|
void enqueue(void* data)
|
|
{
|
|
node_t* n = new node_t;
|
|
n->VAR(next) = 0;
|
|
n->VAR(data) = data;
|
|
bool was_empty = false;
|
|
|
|
pthread_mutex_lock(&mtx);
|
|
if (VAR(head) == 0)
|
|
{
|
|
was_empty = true;
|
|
VAR(head) = n;
|
|
VAR(tail) = n;
|
|
}
|
|
else
|
|
{
|
|
VAR(tail)->VAR(next) = n;
|
|
VAR(tail) = n;
|
|
}
|
|
pthread_mutex_unlock(&mtx);
|
|
|
|
if (was_empty)
|
|
pthread_cond_broadcast(&cv);
|
|
}
|
|
|
|
void* dequeue()
|
|
{
|
|
node_t* n = 0;
|
|
|
|
pthread_mutex_lock(&mtx);
|
|
while (VAR(head) == 0)
|
|
pthread_cond_wait(&cv, &mtx);
|
|
n = VAR(head);
|
|
if (n->VAR(next) == 0)
|
|
VAR(tail) = 0;
|
|
VAR(head) = n->VAR(next);
|
|
pthread_mutex_unlock(&mtx);
|
|
|
|
void* data = n->VAR(data);
|
|
delete n;
|
|
return data;
|
|
}
|
|
|
|
private:
|
|
struct node_t
|
|
{
|
|
VAR_T(node_t*) next;
|
|
VAR_T(void*) data;
|
|
};
|
|
|
|
VAR_T(node_t*) head;
|
|
VAR_T(node_t*) tail;
|
|
|
|
pthread_mutex_t mtx;
|
|
pthread_cond_t cv;
|
|
};
|
|
|
|
void* enqueue_thread(void* ctx)
|
|
{
|
|
queue_t* q = static_cast<queue_t*>(ctx);
|
|
for (size_t i = 0; i != 4; i += 1)
|
|
q->enqueue((void*)(i + 1));
|
|
return 0;
|
|
}
|
|
|
|
void* dequeue_thread(void* ctx)
|
|
{
|
|
queue_t* q = static_cast<queue_t*>(ctx);
|
|
for (size_t i = 0; i != 4; i += 1)
|
|
{
|
|
void* data = q->dequeue();
|
|
assert((int)(uintptr_t)data >= 1 && (int)(uintptr_t)data <= 4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void queue_test()
|
|
{
|
|
queue_t q;
|
|
|
|
pthread_t th [4];
|
|
for (size_t i = 0; i != 2; i += 1)
|
|
pthread_create(&th[i], 0, enqueue_thread, &q);
|
|
for (size_t i = 2; i != 4; i += 1)
|
|
pthread_create(&th[i], 0, dequeue_thread, &q);
|
|
|
|
void* res = 0;
|
|
for (size_t i = 0; i != 4; i += 1)
|
|
pthread_join(th[i], &res);
|
|
}
|
|
|
|
/*
|
|
class recursive_timed_mutex
|
|
{
|
|
public:
|
|
recursive_timed_mutex()
|
|
{
|
|
sema.init(false, 1, 1, $);
|
|
owner = -1;
|
|
recursion_count = 0;
|
|
}
|
|
|
|
~recursive_timed_mutex()
|
|
{
|
|
assert(owner == -1 && recursion_count == 0);
|
|
sema.deinit($);
|
|
}
|
|
|
|
void lock(rl::debug_info_param info)
|
|
{
|
|
rl::context& c = rl::ctx();
|
|
if (owner == c.current_thread())
|
|
{
|
|
RL_HIST(rl::user_msg_event) {"recursive mutex lock"} RL_HIST_END();
|
|
assert(recursion_count > 0);
|
|
recursion_count += 1;
|
|
}
|
|
else
|
|
{
|
|
sema.wait(false, false, info);
|
|
assert(owner == -1 && recursion_count == 0);
|
|
owner = c.current_thread();
|
|
recursion_count = 1;
|
|
}
|
|
}
|
|
|
|
bool try_lock(rl::debug_info_param info)
|
|
{
|
|
rl::context& c = rl::ctx();
|
|
if (owner == c.current_thread())
|
|
{
|
|
RL_HIST(rl::user_msg_event) {"recursive mutex try lock"} RL_HIST_END();
|
|
assert(recursion_count > 0);
|
|
recursion_count += 1;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
rl::sema_wakeup_reason r = sema.wait(true, false, info);
|
|
if (r == rl::sema_wakeup_reason_success)
|
|
{
|
|
assert(owner == -1 && recursion_count == 0);
|
|
owner = c.current_thread();
|
|
recursion_count = 1;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void unlock(rl::debug_info_param info)
|
|
{
|
|
rl::context& c = rl::ctx();
|
|
assert(owner == c.current_thread() && recursion_count > 0);
|
|
RL_HIST(rl::user_msg_event) {"recursive mutex unlock"} RL_HIST_END();
|
|
recursion_count -= 1;
|
|
if (recursion_count == 0)
|
|
{
|
|
owner = -1;
|
|
unsigned prev;
|
|
sema.post(1, prev, info);
|
|
}
|
|
}
|
|
|
|
bool timed_lock(rl::debug_info_param info, ... )
|
|
{
|
|
rl::context& c = rl::ctx();
|
|
if (owner == c.current_thread())
|
|
{
|
|
RL_HIST(rl::user_msg_event) {"recursive mutex timed lock"} RL_HIST_END();
|
|
assert(recursion_count > 0);
|
|
recursion_count += 1;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
rl::sema_wakeup_reason r = sema.wait(false, true, info);
|
|
if (r == rl::sema_wakeup_reason_success)
|
|
{
|
|
assert(owner == -1 && recursion_count == 0);
|
|
owner = c.current_thread();
|
|
recursion_count = 1;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
struct tag_t;
|
|
rl::semaphore<tag_t> sema;
|
|
rl::thread_id_t owner;
|
|
int recursion_count;
|
|
|
|
recursive_timed_mutex(recursive_timed_mutex const&);
|
|
recursive_timed_mutex& operator = (recursive_timed_mutex const&);
|
|
};
|
|
*/
|
|
|
|
class recursive_timed_mutex
|
|
{
|
|
public:
|
|
recursive_timed_mutex()
|
|
{
|
|
mtx = CreateMutex(0, 0, 0);
|
|
}
|
|
|
|
~recursive_timed_mutex()
|
|
{
|
|
CloseHandle(mtx);
|
|
}
|
|
|
|
void lock(rl::debug_info_param info)
|
|
{
|
|
rl::rl_WaitForSingleObject(mtx, INFINITE, info);
|
|
}
|
|
|
|
bool try_lock(rl::debug_info_param info)
|
|
{
|
|
return WAIT_OBJECT_0 == rl::rl_WaitForSingleObject(mtx, 0, info);
|
|
}
|
|
|
|
void unlock(rl::debug_info_param info)
|
|
{
|
|
rl::rl_ReleaseMutex(mtx, info);
|
|
}
|
|
|
|
bool timed_lock(rl::debug_info_param info, ... /*abs_time*/)
|
|
{
|
|
return WAIT_OBJECT_0 == rl::rl_WaitForSingleObject(mtx, 1, info);
|
|
}
|
|
|
|
private:
|
|
HANDLE mtx;
|
|
|
|
recursive_timed_mutex(recursive_timed_mutex const&);
|
|
recursive_timed_mutex& operator = (recursive_timed_mutex const&);
|
|
};
|
|
|
|
|
|
|
|
struct recursive_timed_mutex_test : rl::test_suite<recursive_timed_mutex_test, 3>
|
|
{
|
|
recursive_timed_mutex mtx;
|
|
VAR_T(int) data;
|
|
|
|
void thread(unsigned idx)
|
|
{
|
|
if (idx)
|
|
{
|
|
mtx.lock($);
|
|
mtx.lock($);
|
|
VAR(data) = 1;
|
|
mtx.unlock($);
|
|
mtx.unlock($);
|
|
}
|
|
else
|
|
{
|
|
if (mtx.timed_lock($))
|
|
{
|
|
VAR(data) = 2;
|
|
mtx.unlock($);
|
|
}
|
|
}
|
|
}
|
|
|
|
void after()
|
|
{
|
|
//assert(VAR(data) != 2);
|
|
}
|
|
};
|
|
|
|
|
|
int main()
|
|
{
|
|
//rl::test_params p;
|
|
//p.search_type = rl::sched_full;
|
|
//p.context_bound = 5;
|
|
//p.execution_depth_limit = 200;
|
|
//rl::simulate<test_pthread_condvar>(p);
|
|
//if (rand() <= RAND_MAX) return 0;
|
|
|
|
//rl::execute<queue_test, 4>();
|
|
//if (rand() <= RAND_MAX) return 0;
|
|
|
|
//rl::test_params p;
|
|
//p.initial_state = "1000000";
|
|
//p.iteration_count = 2000000;
|
|
//p.collect_history = true;
|
|
//p.output_history = true;
|
|
//p.search_type = rl::sched_bound;
|
|
//p.search_type = rl::sched_full;
|
|
//p.execution_depth_limit = 500;
|
|
//p.context_bound = 1;
|
|
//rl::simulate<test_pthread_condvar>(p);
|
|
//std::cout << "scheduler state = \"" << p.final_state << "\"" << std::endl;
|
|
//std::cout << std::endl;
|
|
//if (rand() <= RAND_MAX) return 0;
|
|
|
|
//rl::test_params p;
|
|
//p.iteration_count = 80000000;
|
|
//p.initial_state = "50000000";
|
|
//p.search_type = rl::fair_context_bound_scheduler_type;
|
|
//p.context_bound = 1;
|
|
//p.collect_history = true;
|
|
//p.output_history = true;
|
|
//rl::simulate<test>(p);
|
|
//if (rand() <= RAND_MAX) return 0;
|
|
|
|
//rl::test_params p;
|
|
//p.context_bound = 1;
|
|
//p.iteration_count = 1000;
|
|
//p.search_type = rl::fair_full_search_scheduler_type;
|
|
//p.search_type = rl::random_scheduler_type;
|
|
//p.collect_history = true;
|
|
//p.output_history = true;
|
|
//p.execution_depth_limit = 1000;
|
|
//p.initial_state = "550 24 3 0 0 3 0 0 3 0 0 3 0 0 2 0 4 2 0 0 2 0 4 2 1 0 2 0 4 3 1 0 3 0 0 2 0 0 1 0 4 2 0 4 3 0 0 3 0 0 2 0 4 3 1 0 3 0 0 2 1 0 2 0 4 2 1 0 2 1 0 2 1 4";
|
|
//bool result = rl::simulate<test>(p);
|
|
//std::cout << "result=" << result << std::endl;
|
|
//simulate<my_test>();
|
|
//if (rand() <= RAND_MAX) return 0;
|
|
|
|
rl::simulate_f tests[] =
|
|
{
|
|
#if 1
|
|
&rl::simulate<test_FlushProcessWriteBuffers>,
|
|
|
|
&rl::simulate<test_addr_hash>,
|
|
&rl::simulate<test_addr_hash2>,
|
|
//!!! fails &rl::simulate<sched_load_test>,
|
|
&rl::simulate<test_memory_allocation>,
|
|
|
|
// memory model
|
|
&rl::simulate<test_pthread_thread>,
|
|
&rl::simulate<test_pthread_mutex>,
|
|
&rl::simulate<test_pthread_rwlock>,
|
|
&rl::simulate<test_pthread_condvar>,
|
|
&rl::simulate<test_pthread_condvar2>,
|
|
&rl::simulate<test_pthread_sem>,
|
|
|
|
&rl::simulate<coherent_read_read_test>,
|
|
&rl::simulate<order_relaxed_test<0> >,
|
|
&rl::simulate<order_relaxed_test<1> >,
|
|
&rl::simulate<order_relaxed_test<2> >,
|
|
&rl::simulate<order_relaxed_test<3> >,
|
|
&rl::simulate<order_relaxed_test<4> >,
|
|
&rl::simulate<reorder_single_var_test>,
|
|
&rl::simulate<acq_rel_test>,
|
|
|
|
&rl::simulate<seq_cst_test<0> >,
|
|
&rl::simulate<seq_cst_test<1> >,
|
|
|
|
&rl::simulate<reordering_test>,
|
|
&rl::simulate<reordering_test2>,
|
|
|
|
&rl::simulate<test_win_thread>,
|
|
&rl::simulate<test_win_mutex>,
|
|
&rl::simulate<test_win_cs>,
|
|
&rl::simulate<test_win_condvar>,
|
|
&rl::simulate<test_win_condvar_srw>,
|
|
&rl::simulate<test_win_sem>,
|
|
&rl::simulate<test_win_event>,
|
|
|
|
&rl::simulate<modification_order_test>,
|
|
&rl::simulate<transitive_test>,
|
|
&rl::simulate<cc_transitive_test>,
|
|
&rl::simulate<occasional_test>,
|
|
|
|
// fences
|
|
&rl::simulate<fence_synch_test<0, 0> >,
|
|
&rl::simulate<fence_synch_test<1, 0> >,
|
|
&rl::simulate<fence_synch_test<2, 0> >,
|
|
&rl::simulate<fence_synch_test<0, 1> >,
|
|
&rl::simulate<fence_synch_test<1, 1> >,
|
|
&rl::simulate<fence_synch_test<2, 1> >,
|
|
|
|
&rl::simulate<two_fence_synch_test>,
|
|
&rl::simulate<seq_cst_fence_test<0> >,
|
|
&rl::simulate<seq_cst_fence_test<1> >,
|
|
|
|
// data races
|
|
&rl::simulate<race_ld_ld_test>,
|
|
&rl::simulate<race_ld_st_test>,
|
|
&rl::simulate<race_st_st_test>,
|
|
|
|
&rl::simulate<race_seq_ld_ld_test>,
|
|
&rl::simulate<race_seq_ld_st_test>,
|
|
&rl::simulate<race_seq_st_ld_test>,
|
|
&rl::simulate<race_seq_st_st_test>,
|
|
|
|
&rl::simulate<race_uninit_test>,
|
|
&rl::simulate<race_indirect_test>,
|
|
|
|
// compare_exchange
|
|
&rl::simulate<cas_spurious_fail_test<0> >,
|
|
&rl::simulate<cas_spurious_fail_test<1> >,
|
|
&rl::simulate<cas_spurious_fail_test<2> >,
|
|
|
|
// mutex
|
|
&rl::simulate<test_deadlock>,
|
|
&rl::simulate<test_deadlock2>,
|
|
&rl::simulate<test_mutex_destuction>,
|
|
&rl::simulate<test_mutex_destuction2>,
|
|
&rl::simulate<test_mutex_recursion>,
|
|
&rl::simulate<test_mutex_recursion_error>,
|
|
&rl::simulate<test_mutex_unlock_error>,
|
|
&rl::simulate<test_mutex_leak>,
|
|
&rl::simulate<test_mutex>,
|
|
&rl::simulate<test_mutex_try_lock>,
|
|
|
|
// futex
|
|
&rl::simulate<test_futex>,
|
|
&rl::simulate<test_futex_deadlock>,
|
|
&rl::simulate<test_futex_sync1>,
|
|
&rl::simulate<test_futex_sync2>,
|
|
&rl::simulate<test_futex_intr>,
|
|
|
|
// condition variable
|
|
&rl::simulate<test_condvar>,
|
|
&rl::simulate<test_condvar2>,
|
|
|
|
// semaphore
|
|
&rl::simulate<test_semaphore>,
|
|
&rl::simulate<test_semaphore_atomic>,
|
|
|
|
// event
|
|
&rl::simulate<test_event_auto>,
|
|
&rl::simulate<test_event_manual>,
|
|
&rl::simulate<test_event_atomic>,
|
|
|
|
//wfmo
|
|
&rl::simulate<test_wfmo_all>,
|
|
&rl::simulate<test_wfmo_single>,
|
|
&rl::simulate<test_wfmo_timeout>,
|
|
&rl::simulate<test_wfmo_try>,
|
|
&rl::simulate<test_wfmo_mixed>,
|
|
&rl::simulate<test_wfmo_mixed2>,
|
|
&rl::simulate<test_wfmo_event_all>,
|
|
&rl::simulate<test_wfmo_event_any>,
|
|
&rl::simulate<test_wfmo_atomic>,
|
|
|
|
// thread local storage
|
|
&rl::simulate<tls_basic_test>,
|
|
&rl::simulate<tls_reset_test>,
|
|
&rl::simulate<tls_global_test>,
|
|
&rl::simulate<tls_win32_test>,
|
|
|
|
// dynamic thread
|
|
&rl::simulate<dyn_thread_basic_test>,
|
|
&rl::simulate<dyn_thread_win32_test>,
|
|
&rl::simulate<dyn_thread_visibility_test>,
|
|
#endif
|
|
};
|
|
|
|
for (size_t sched = 0; sched != rl::sched_count; ++sched)
|
|
{
|
|
std::cout << format((rl::scheduler_type_e)sched) << " tests:" << std::endl;
|
|
|
|
for (size_t i = 0; i != sizeof(tests)/sizeof(*tests); ++i)
|
|
{
|
|
//!!! make it work under sched_full
|
|
if (sched == rl::sched_full
|
|
&& (tests[i] == (rl::simulate_f)&rl::simulate<test_pthread_condvar>
|
|
|| tests[i] == (rl::simulate_f)&rl::simulate<test_win_condvar>))
|
|
continue;
|
|
|
|
rl::ostringstream stream;
|
|
rl::test_params params;
|
|
params.search_type = (rl::scheduler_type_e)sched;
|
|
params.iteration_count =
|
|
(params.test_result == rl::test_result_success ? 100000 : 500);
|
|
params.output_stream = &stream;
|
|
params.progress_stream = &stream;
|
|
params.context_bound = 2;
|
|
params.execution_depth_limit = 500;
|
|
|
|
if (false == tests[i](params))
|
|
{
|
|
std::cout << std::endl;
|
|
std::cout << "FAILED" << std::endl;
|
|
std::cout << stream.str();
|
|
std::cout << std::endl;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
std::cout << params.test_name << "...OK" << std::endl;
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
rl::simulate_f scheduler_tests[] =
|
|
{
|
|
&rl::simulate<livelock_test>,
|
|
&rl::simulate<yield_livelock_test>,
|
|
};
|
|
|
|
std::cout << "full search scheduler tests:" << std::endl;
|
|
for (size_t i = 0; i != sizeof(scheduler_tests)/sizeof(*scheduler_tests); ++i)
|
|
{
|
|
rl::ostringstream stream;
|
|
rl::test_params params;
|
|
params.search_type = rl::sched_full;
|
|
params.output_stream = &stream;
|
|
params.progress_stream = &stream;
|
|
params.context_bound = 2;
|
|
params.execution_depth_limit = 500;
|
|
|
|
if (false == scheduler_tests[i](params))
|
|
{
|
|
std::cout << std::endl;
|
|
std::cout << "FAILED" << std::endl;
|
|
std::cout << stream.str();
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
std::cout << params.test_name << "...OK" << std::endl;
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "SUCCESS" << std::endl;
|
|
}
|
|
|
|
|
|
|