Skip to content

Instantly share code, notes, and snippets.

@parched
Last active September 28, 2018 21:57
Show Gist options
  • Save parched/b7fb88c97755a81e5cb9f9048a15f7fb to your computer and use it in GitHub Desktop.
Save parched/b7fb88c97755a81e5cb9f9048a15f7fb to your computer and use it in GitHub Desktop.
thread park unpark test
double_unpark_testI8InnerOldE
DEADLOCK (deadlock detected)
iteration: 1377
execution history (24):
[0] 0: [CTOR BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[1] 0: <0x5aabbced00> atomic store, value=0, (prev value=0), order=seq_cst, in InnerOld, test.cpp(205)
[2] 0: [CTOR END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[3] 0: [BEFORE BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[4] 0: <0x5aabbced50> atomic store, value=0, (prev value=0), order=seq_cst, in before, test.cpp(268)
[5] 0: [BEFORE END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[6] 0: <0x5aabbced50> atomic load, value=0, order=relaxed, in thread, test.cpp(271)
[7] 0: <0x5aabbced00> CAS fail orig=0, cmp=1, xchg=0, order=seq_cst, in park, test.cpp(209)
[8] 0: <0x5aabc27498> mutex: exclusive lock, in park, test.cpp(213)
[9] 1: <0x5aabbced00> CAS succ [ABA] orig=0, cmp=0, xchg=1, order=seq_cst, in unpark, test.cpp(239)
[10] 0: <0x5aabbced00> CAS fail orig=1, cmp=0, xchg=2, order=seq_cst, in park, test.cpp(216)
[11] 1: <0x5aabbced50> atomic store, value=1, (prev value=0), order=relaxed, in thread, test.cpp(276)
[12] 1: <0x5aabbced00> CAS fail orig=1, cmp=0, xchg=1, order=seq_cst, in unpark, test.cpp(239)
[13] 1: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[14] 0: <0x5aabbced00> atomic store, value=0, (prev value=1), order=seq_cst, in park, test.cpp(218)
[15] 0: <0x5aabc27498> mutex: exclusive unlock, in park, test.cpp(219)
[16] 0: <0x5aabbced50> atomic load, value=0 [NOT CURRENT], order=relaxed, in thread, test.cpp(271)
[17] 0: <0x5aabbced00> CAS fail orig=0, cmp=1, xchg=0, order=seq_cst, in park, test.cpp(209)
[18] 0: <0x5aabc27498> mutex: exclusive lock, in park, test.cpp(213)
[19] 0: <0x5aabbced00> CAS succ orig=0, cmp=0, xchg=2, order=seq_cst, in park, test.cpp(216)
[20] 0: <0x5aabc035f8> cond_var: wait enter, in park, test.cpp(223)
[21] 0: <0x5aabc27498> mutex: exclusive unlock, in park, test.cpp(223)
[22] 0: blocking current thread, in park, test.cpp(223)
[23] 0: DEADLOCK (deadlock detected), in park, test.cpp(223)
thread 0:
[0] 0: [CTOR BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[1] 0: <0x5aabbced00> atomic store, value=0, (prev value=0), order=seq_cst, in InnerOld, test.cpp(205)
[2] 0: [CTOR END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[3] 0: [BEFORE BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[4] 0: <0x5aabbced50> atomic store, value=0, (prev value=0), order=seq_cst, in before, test.cpp(268)
[5] 0: [BEFORE END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[6] 0: <0x5aabbced50> atomic load, value=0, order=relaxed, in thread, test.cpp(271)
[7] 0: <0x5aabbced00> CAS fail orig=0, cmp=1, xchg=0, order=seq_cst, in park, test.cpp(209)
[8] 0: <0x5aabc27498> mutex: exclusive lock, in park, test.cpp(213)
[10] 0: <0x5aabbced00> CAS fail orig=1, cmp=0, xchg=2, order=seq_cst, in park, test.cpp(216)
[14] 0: <0x5aabbced00> atomic store, value=0, (prev value=1), order=seq_cst, in park, test.cpp(218)
[15] 0: <0x5aabc27498> mutex: exclusive unlock, in park, test.cpp(219)
[16] 0: <0x5aabbced50> atomic load, value=0 [NOT CURRENT], order=relaxed, in thread, test.cpp(271)
[17] 0: <0x5aabbced00> CAS fail orig=0, cmp=1, xchg=0, order=seq_cst, in park, test.cpp(209)
[18] 0: <0x5aabc27498> mutex: exclusive lock, in park, test.cpp(213)
[19] 0: <0x5aabbced00> CAS succ orig=0, cmp=0, xchg=2, order=seq_cst, in park, test.cpp(216)
[20] 0: <0x5aabc035f8> cond_var: wait enter, in park, test.cpp(223)
[21] 0: <0x5aabc27498> mutex: exclusive unlock, in park, test.cpp(223)
[22] 0: blocking current thread, in park, test.cpp(223)
[23] 0: DEADLOCK (deadlock detected), in park, test.cpp(223)
thread 1:
[9] 1: <0x5aabbced00> CAS succ [ABA] orig=0, cmp=0, xchg=1, order=seq_cst, in unpark, test.cpp(239)
[11] 1: <0x5aabbced50> atomic store, value=1, (prev value=0), order=relaxed, in thread, test.cpp(276)
[12] 1: <0x5aabbced00> CAS fail orig=1, cmp=0, xchg=1, order=seq_cst, in unpark, test.cpp(239)
[13] 1: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
post_testI8InnerOldE
iterations: 58
total time: 1
throughput: 58000
ping_pong_testI8InnerOldE
iterations: 586
total time: 10
throughput: 58600
iriw_testI8InnerOldE
iterations: 1000
total time: 1
throughput: 1000000
iriw_seq_cst_testI8InnerOldE
iterations: 1000
total time: 10
throughput: 100000
double_unpark_testI8InnerNewE
iterations: 5117
total time: 30
throughput: 170566
post_testI8InnerNewE
iterations: 58
total time: 1
throughput: 58000
ping_pong_testI8InnerNewE
iterations: 586
total time: 1
throughput: 586000
iriw_testI8InnerNewE
iterations: 1000
total time: 10
throughput: 100000
iriw_seq_cst_testI8InnerNewE
iterations: 1000
total time: 10
throughput: 100000
double_unpark_testI10InnerNewV2E
iterations: 1655
total time: 10
throughput: 165500
post_testI10InnerNewV2E
iterations: 26
total time: 1
throughput: 26000
ping_pong_testI10InnerNewV2E
iterations: 282
total time: 1
throughput: 282000
iriw_testI10InnerNewV2E
iterations: 1000
total time: 1
throughput: 1000000
iriw_seq_cst_testI10InnerNewV2E
iterations: 1000
total time: 10
throughput: 100000
double_unpark_testI10InnerNewV3E
iterations: 1655
total time: 10
throughput: 165500
post_testI10InnerNewV3E
iterations: 26
total time: 1
throughput: 26000
ping_pong_testI10InnerNewV3E
iterations: 282
total time: 1
throughput: 282000
iriw_testI10InnerNewV3E
iterations: 1000
total time: 10
throughput: 100000
iriw_seq_cst_testI10InnerNewV3E
iterations: 1000
total time: 10
throughput: 100000
double_unpark_testI10InnerNewV4E
iterations: 1655
total time: 1
throughput: 1655000
post_testI10InnerNewV4E
iterations: 26
total time: 1
throughput: 26000
ping_pong_testI10InnerNewV4E
iterations: 282
total time: 1
throughput: 282000
iriw_testI10InnerNewV4E
USER ASSERT FAILED (assertion: !(!read_2_0 && read_2_1 && read_3_0 && !read_3_1))
iteration: 405
execution history (38):
[0] 0: [CTOR BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[1] 0: <0x5aabc00300> atomic store, value=0, (prev value=0), order=seq_cst, in InnerNewV4, test.cpp(13)
[2] 0: <0x5aabc00350> atomic store, value=0, (prev value=0), order=seq_cst, in InnerNewV4, test.cpp(13)
[3] 0: [CTOR END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[4] 0: [BEFORE BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[5] 0: <0x5aabc002c0> atomic store, value=0, (prev value=0), order=seq_cst, in before, test.cpp(327)
[6] 0: <0x5aabc002e0> atomic store, value=0, (prev value=0), order=seq_cst, in before, test.cpp(327)
[7] 0: [BEFORE END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[8] 2: <0x5aabc00300> CAS fail orig=0, cmp=1, xchg=0, order=acquire, in park, test.cpp(17)
[9] 2: <0x5aac0769f8> mutex: exclusive lock, in park, test.cpp(21)
[10] 1: <0x5aabc002e0> atomic store, value=1, (prev value=0), order=relaxed, in thread, test.cpp(334)
[11] 2: <0x5aabc00300> CAS succ orig=0, cmp=0, xchg=2, order=relaxed, in park, test.cpp(24)
[12] 2: <0x5aac07cbc8> cond_var: wait enter, in park, test.cpp(31)
[13] 1: <0x5aabc00300> exchange , prev=2, arg=1, new=1, order=release, in unpark, test.cpp(45)
[14] 2: <0x5aac0769f8> mutex: exclusive unlock, in park, test.cpp(31)
[15] 2: blocking current thread, in park, test.cpp(31)
[16] 2: <0x5aac07cbc8> cond_var: wait exit spuriously, in park, test.cpp(31)
[17] 2: <0x5aac0769f8> mutex: exclusive lock, in park, test.cpp(31)
[18] 2: <0x5aabc00300> CAS succ [ABA] orig=1, cmp=1, xchg=0, order=acquire, in park, test.cpp(34)
[19] 2: <0x5aac0769f8> mutex: exclusive unlock, in park, test.cpp(36)
[20] 2: <0x5aabc002c0> atomic load, value=0, order=relaxed, in thread, test.cpp(339)
[21] 2: <0x5aabc002e0> atomic load, value=1, order=relaxed, in thread, test.cpp(340)
[22] 2: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[23] 0: <0x5aabc002c0> atomic store, value=1, (prev value=0), order=relaxed, in thread, test.cpp(330)
[24] 0: <0x5aabc00300> exchange , prev=0, arg=1, new=1, order=release, in unpark, test.cpp(45)
[25] 0: <0x5aabc00350> exchange , prev=0, arg=1, new=1, order=release, in unpark, test.cpp(45)
[26] 0: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[27] 1: <0x5aac0769f8> mutex: exclusive lock, in unpark, test.cpp(49)
[28] 3: <0x5aabc00350> CAS succ [ABA] orig=1, cmp=1, xchg=0, order=acquire, in park, test.cpp(17)
[29] 1: <0x5aac0769f8> mutex: exclusive unlock, in unpark, test.cpp(50)
[30] 1: <0x5aac07cbc8> cond_var: notify one total_blocked=0 unblocked=0, in unpark, test.cpp(51)
[31] 3: <0x5aabc002c0> atomic load, value=1, order=relaxed, in thread, test.cpp(343)
[32] 3: <0x5aabc002e0> atomic load, value=0 [NOT CURRENT], order=relaxed, in thread, test.cpp(344)
[33] 3: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[34] 1: <0x5aabc00350> exchange , prev=0, arg=1, new=1, order=release, in unpark, test.cpp(45)
[35] 1: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[36] 1: [AFTER BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[37] 1: USER ASSERT FAILED (assertion: !(!read_2_0 && read_2_1 && read_3_0 && !read_3_1)), in after, test.cpp(349)
thread 0:
[0] 0: [CTOR BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[1] 0: <0x5aabc00300> atomic store, value=0, (prev value=0), order=seq_cst, in InnerNewV4, test.cpp(13)
[2] 0: <0x5aabc00350> atomic store, value=0, (prev value=0), order=seq_cst, in InnerNewV4, test.cpp(13)
[3] 0: [CTOR END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[4] 0: [BEFORE BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[5] 0: <0x5aabc002c0> atomic store, value=0, (prev value=0), order=seq_cst, in before, test.cpp(327)
[6] 0: <0x5aabc002e0> atomic store, value=0, (prev value=0), order=seq_cst, in before, test.cpp(327)
[7] 0: [BEFORE END], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[23] 0: <0x5aabc002c0> atomic store, value=1, (prev value=0), order=relaxed, in thread, test.cpp(330)
[24] 0: <0x5aabc00300> exchange , prev=0, arg=1, new=1, order=release, in unpark, test.cpp(45)
[25] 0: <0x5aabc00350> exchange , prev=0, arg=1, new=1, order=release, in unpark, test.cpp(45)
[26] 0: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
thread 1:
[10] 1: <0x5aabc002e0> atomic store, value=1, (prev value=0), order=relaxed, in thread, test.cpp(334)
[13] 1: <0x5aabc00300> exchange , prev=2, arg=1, new=1, order=release, in unpark, test.cpp(45)
[27] 1: <0x5aac0769f8> mutex: exclusive lock, in unpark, test.cpp(49)
[29] 1: <0x5aac0769f8> mutex: exclusive unlock, in unpark, test.cpp(50)
[30] 1: <0x5aac07cbc8> cond_var: notify one total_blocked=0 unblocked=0, in unpark, test.cpp(51)
[34] 1: <0x5aabc00350> exchange , prev=0, arg=1, new=1, order=release, in unpark, test.cpp(45)
[35] 1: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[36] 1: [AFTER BEGIN], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
[37] 1: USER ASSERT FAILED (assertion: !(!read_2_0 && read_2_1 && read_3_0 && !read_3_1)), in after, test.cpp(349)
thread 2:
[8] 2: <0x5aabc00300> CAS fail orig=0, cmp=1, xchg=0, order=acquire, in park, test.cpp(17)
[9] 2: <0x5aac0769f8> mutex: exclusive lock, in park, test.cpp(21)
[11] 2: <0x5aabc00300> CAS succ orig=0, cmp=0, xchg=2, order=relaxed, in park, test.cpp(24)
[12] 2: <0x5aac07cbc8> cond_var: wait enter, in park, test.cpp(31)
[14] 2: <0x5aac0769f8> mutex: exclusive unlock, in park, test.cpp(31)
[15] 2: blocking current thread, in park, test.cpp(31)
[16] 2: <0x5aac07cbc8> cond_var: wait exit spuriously, in park, test.cpp(31)
[17] 2: <0x5aac0769f8> mutex: exclusive lock, in park, test.cpp(31)
[18] 2: <0x5aabc00300> CAS succ [ABA] orig=1, cmp=1, xchg=0, order=acquire, in park, test.cpp(34)
[19] 2: <0x5aac0769f8> mutex: exclusive unlock, in park, test.cpp(36)
[20] 2: <0x5aabc002c0> atomic load, value=0, order=relaxed, in thread, test.cpp(339)
[21] 2: <0x5aabc002e0> atomic load, value=1, order=relaxed, in thread, test.cpp(340)
[22] 2: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
thread 3:
[28] 3: <0x5aabc00350> CAS succ [ABA] orig=1, cmp=1, xchg=0, order=acquire, in park, test.cpp(17)
[31] 3: <0x5aabc002c0> atomic load, value=1, order=relaxed, in thread, test.cpp(343)
[32] 3: <0x5aabc002e0> atomic load, value=0 [NOT CURRENT], order=relaxed, in thread, test.cpp(344)
[33] 3: [THREAD FINISHED], in fiber_proc_impl, ../relacy/relacy/context.hpp(457)
iriw_seq_cst_testI10InnerNewV4E
iterations: 1000
total time: 10
throughput: 100000
#include <relacy/relacy.hpp>
constexpr int EMPTY = 0;
constexpr int NOTIFIED = 1;
constexpr int PARKED = 2;
// non sequentially constistent version of V3
struct InnerNewV4 {
rl::atomic<int> state;
rl::mutex lock;
rl::condition_variable cvar;
InnerNewV4() { state($) = EMPTY; }
void park() {
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY, rl::memory_order_acquire,
rl::memory_order_relaxed)) {
return;
}
lock.lock($);
expected = EMPTY;
if (!state.compare_exchange_strong(expected, PARKED,
rl::memory_order_relaxed,
rl::memory_order_relaxed)) {
state.exchange(EMPTY, rl::memory_order_acquire);
lock.unlock($);
return;
}
while (1) {
cvar.wait(lock, $);
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY,
rl::memory_order_acquire,
rl::memory_order_relaxed)) {
lock.unlock($);
return;
}
}
lock.unlock($);
}
void unpark() {
int expected = EMPTY;
if (state.exchange(NOTIFIED, rl::memory_order_release) != PARKED) {
return;
}
lock.lock($);
lock.unlock($);
cvar.notify_one($);
}
};
// Relaxed version of V2
struct InnerNewV3 {
rl::atomic<int> state;
rl::mutex lock;
rl::condition_variable cvar;
InnerNewV3() { state($) = EMPTY; }
void park() {
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY, rl::memory_order_seq_cst,
rl::memory_order_relaxed)) {
return;
}
lock.lock($);
expected = EMPTY;
if (!state.compare_exchange_strong(expected, PARKED,
rl::memory_order_relaxed,
rl::memory_order_relaxed)) {
state.exchange(EMPTY, rl::memory_order_acquire);
lock.unlock($);
return;
}
while (1) {
cvar.wait(lock, $);
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY,
rl::memory_order_seq_cst,
rl::memory_order_relaxed)) {
lock.unlock($);
return;
}
}
lock.unlock($);
}
void unpark() {
int expected = EMPTY;
if (state.exchange(NOTIFIED, rl::memory_order_seq_cst) != PARKED) {
return;
}
lock.lock($);
lock.unlock($);
cvar.notify_one($);
}
};
// unpark doesn't hold lock while notifying
struct InnerNewV2 {
rl::atomic<int> state;
rl::mutex lock;
rl::condition_variable cvar;
InnerNewV2() { state($) = EMPTY; }
void park() {
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY, rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
return;
}
lock.lock($);
expected = EMPTY;
if (!state.compare_exchange_strong(expected, PARKED,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
state.exchange(EMPTY, rl::memory_order_seq_cst);
lock.unlock($);
return;
}
while (1) {
cvar.wait(lock, $);
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
lock.unlock($);
return;
}
}
lock.unlock($);
}
void unpark() {
int expected = EMPTY;
if (state.exchange(NOTIFIED, rl::memory_order_seq_cst) != PARKED) {
return;
}
lock.lock($);
lock.unlock($);
cvar.notify_one($);
}
};
struct InnerNew {
rl::atomic<int> state;
rl::mutex lock;
rl::condition_variable cvar;
InnerNew() { state($) = EMPTY; }
void park() {
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY, rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
return;
}
lock.lock($);
expected = EMPTY;
if (!state.compare_exchange_strong(expected, PARKED,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
state.exchange(EMPTY, rl::memory_order_seq_cst);
lock.unlock($);
return;
}
while (1) {
cvar.wait(lock, $);
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
lock.unlock($);
return;
}
}
lock.unlock($);
}
void unpark() {
int expected = EMPTY;
if (state.exchange(NOTIFIED, rl::memory_order_seq_cst) != PARKED) {
return;
}
lock.lock($);
cvar.notify_one($);
lock.unlock($);
}
};
struct InnerOld {
rl::atomic<int> state;
rl::mutex lock;
rl::condition_variable cvar;
InnerOld() { state($) = EMPTY; }
void park() {
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY, rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
return;
}
lock.lock($);
expected = EMPTY;
if (!state.compare_exchange_strong(expected, PARKED,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
state.store(EMPTY, rl::memory_order_seq_cst);
lock.unlock($);
return;
}
while (1) {
cvar.wait(lock, $);
int expected = NOTIFIED;
if (state.compare_exchange_strong(expected, EMPTY,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
lock.unlock($);
return;
}
}
lock.unlock($);
}
void unpark() {
while (1) {
int expected = EMPTY;
if (state.compare_exchange_strong(expected, NOTIFIED,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
return;
} else if (expected == NOTIFIED) {
return;
}
lock.lock($);
expected = PARKED;
if (state.compare_exchange_strong(expected, NOTIFIED,
rl::memory_order_seq_cst,
rl::memory_order_seq_cst)) {
cvar.notify_one($);
lock.unlock($);
return;
} else if (expected == NOTIFIED) {
lock.unlock($);
return;
}
lock.unlock($);
}
}
};
// original bug
//
template <typename I> struct double_unpark_test : rl::test_suite<double_unpark_test<I>, 2> {
I inner;
rl::atomic<bool> data;
void before() { data($) = false; }
void thread(unsigned id) {
if (id == 0) {
while (!data.load(rl::memory_order_relaxed)) {
inner.park();
}
} else {
inner.unpark();
data.store(true, rl::memory_order_relaxed);
inner.unpark();
}
}
};
// verify non-atomic data posted before unpark is called is observed
// when park returns
// this is allowed to fail because spurious wakes are allowed
// but all these implementations should not do it
template <typename I> struct post_test : rl::test_suite<post_test<I>, 2> {
I inner;
rl::var<bool> data;
void before() { data($) = false; }
void thread(unsigned id) {
if (id == 0) {
inner.park();
RL_ASSERT(data($));
} else {
data($) = true;
inner.unpark();
}
}
};
// idk what this is really testing but it shouldn't deadlock
template <typename I> struct ping_pong_test : rl::test_suite<ping_pong_test<I>, 2> {
I inner0;
I inner1;
void thread(unsigned id) {
if (id == 0) {
inner1.unpark();
inner0.park();
} else {
inner0.unpark();
inner1.park();
}
}
};
// Independent read independent write
// I don't think this is something park needs to garauntee, mutex doesn't
template <typename I> struct iriw_test : rl::test_suite<iriw_test<I>, 4> {
rl::atomic<bool> data0;
rl::atomic<bool> data1;
I inner2;
I inner3;
bool read_2_0 = false;
bool read_2_1 = false;
bool read_3_0 = false;
bool read_3_1 = false;
void before() { data0($) = false; data1($) = false; }
void thread(unsigned id) {
if (id == 0) {
data0.store(true, rl::memory_order_relaxed);
inner2.unpark();
inner3.unpark();
} if (id == 1) {
data1.store(true, rl::memory_order_relaxed);
inner2.unpark();
inner3.unpark();
} if (id == 2) {
inner2.park();
read_2_0 = data0.load(rl::memory_order_relaxed);
read_2_1 = data1.load(rl::memory_order_relaxed);
} if (id == 3) {
inner3.park();
read_3_0 = data0.load(rl::memory_order_relaxed);
read_3_1 = data1.load(rl::memory_order_relaxed);
}
}
void after() {
RL_ASSERT(!(read_2_0 && !read_2_1 && !read_3_0 && read_3_1));
RL_ASSERT(!(!read_2_0 && read_2_1 && read_3_0 && !read_3_1));
}
};
// Independent read independent write with seq cst fences
// This should be garaunteed
template <typename I> struct iriw_seq_cst_test : rl::test_suite<iriw_seq_cst_test<I>, 4> {
rl::atomic<bool> data0;
rl::atomic<bool> data1;
I inner2;
I inner3;
bool read_2_0 = false;
bool read_2_1 = false;
bool read_3_0 = false;
bool read_3_1 = false;
void before() { data0($) = false; data1($) = false; }
void thread(unsigned id) {
if (id == 0) {
data0.store(true, rl::memory_order_relaxed);
rl::atomic_thread_fence(rl::memory_order_seq_cst);
inner2.unpark();
inner3.unpark();
} if (id == 1) {
data1.store(true, rl::memory_order_relaxed);
rl::atomic_thread_fence(rl::memory_order_seq_cst);
inner2.unpark();
inner3.unpark();
} if (id == 2) {
inner2.park();
rl::atomic_thread_fence(rl::memory_order_seq_cst);
read_2_0 = data0.load(rl::memory_order_relaxed);
read_2_1 = data1.load(rl::memory_order_relaxed);
} if (id == 3) {
inner3.park();
rl::atomic_thread_fence(rl::memory_order_seq_cst);
read_3_0 = data0.load(rl::memory_order_relaxed);
read_3_1 = data1.load(rl::memory_order_relaxed);
}
}
void after() {
RL_ASSERT(!(read_2_0 && !read_2_1 && !read_3_0 && read_3_1));
RL_ASSERT(!(!read_2_0 && read_2_1 && read_3_0 && !read_3_1));
}
};
template <typename T> void run_test() {
rl::test_params test_params;
test_params.search_type = rl::sched_full;
rl::simulate<T>(test_params);
}
template <typename I> void run_tests() {
run_test<double_unpark_test<I>>();
run_test<post_test<I>>();
run_test<ping_pong_test<I>>();
// this too complex to do a full search :(
rl::simulate<iriw_test<I>>();
rl::simulate<iriw_seq_cst_test<I>>();
}
int main() {
run_tests<InnerOld>();
run_tests<InnerNew>();
run_tests<InnerNewV2>();
run_tests<InnerNewV3>();
run_tests<InnerNewV4>();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment