#include #include #include int main () { cruft::TAP::logger tap; cruft::thread::flag f; std::atomic value = 0; std::thread t1 ([&] () { f.wait (); value = 1; }); std::this_thread::sleep_for (std::chrono::milliseconds (100)); tap.expect_eq (value, 0, "value hasn't been set during wait"); f.notify_all (); t1.join (); tap.expect_eq (value, 1, "value has been changed after wait"); std::thread t2 ([&] () { f.wait (); value = 2; }); std::this_thread::sleep_for (std::chrono::milliseconds (100)); tap.expect_eq (value, 2, "second wait didn't appear to block"); t2.join (); { // perform a stress test to (hopefully) discover deadlocks // // * create a large matrix of flag variables // * create a bank of threads which: // * wait on each flag of each row, or // * notify if the flag index matches the thread index constexpr int iterations = 1024; constexpr int parallelism = 16; std::vector< std::array > flags (iterations); const auto func = [&flags] (const int idx) { for (auto &row: flags) { for (int i = 0; i < parallelism; ++i) { if (i == idx) row[i].notify_all (); else row[i].wait (); } } }; std::vector workers; std::generate_n ( std::back_inserter (workers), parallelism, [&, i = 0] () mutable { return std::thread { func, i++ }; }); for (auto &t: workers) t.join (); tap.expect (true, "flag sequence did not block"); } return tap.status (); }