-
-
Save parched/b7fb88c97755a81e5cb9f9048a15f7fb to your computer and use it in GitHub Desktop.
thread park unpark test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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