#include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// void fight (cruft::thread::semaphore &sem, const int iterations) { for (int i = 0; i < iterations; ++i) { std::lock_guard tmp {sem}; (void)tmp; } } /////////////////////////////////////////////////////////////////////////////// int main () { cruft::TAP::logger tap; { cruft::thread::semaphore sem (0); tap.expect_eq (sem.value (), 0, "initialisation is respected"); tap.expect_eq (sem.unlock (), 1, "bare release increments without blocking"); tap.expect_eq (sem.lock (), 0, "bare acquire decrements without blocking"); } // test that two threads can cooperate on a single semaphore. { cruft::thread::semaphore sem (1); // the spawned thread attempts to double acquire a semaphore with // only one available and so should get blocked immediately. std::atomic test = 0; std::thread t ([&] () { sem.lock (); sem.lock (); test = 1; }); // wait until we know the thread should have been blocked. it should // not have touched the 'test' variable at this point. while (sem.value () > 0) std::this_thread::yield (); tap.expect_eq (test, 0, "locking blocks in foreign thread"); // unlock the semaphore, wait for the thread to finish, and check it // touched the 'test' variable. sem.unlock (); t.join (); tap.expect_eq (test, 1, "unlocking resumes foreign thread"); } { const auto parallelism = std::thread::hardware_concurrency (); constexpr int iterations = 1 << 16; std::vector threads; cruft::thread::semaphore sem (0); for (unsigned i = 0; i < parallelism; ++i) threads.emplace_back (fight, std::ref (sem), iterations); sem.unlock (); sem.unlock (); for (auto &t: threads) t.join (); tap.expect (true, "high concurrency didn't deadlock"); } return tap.status (); }