/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 Danny Robson */ #include "thread/flag.hpp" #include "thread/ticketlock.hpp" #include "tap.hpp" #include #include /////////////////////////////////////////////////////////////////////////////// using ffs_t = std::chrono::high_resolution_clock; //----------------------------------------------------------------------------- void fight (cruft::thread::flag &start, cruft::thread::ticketlock &l, int total, ffs_t::time_point &finish) { start.wait (); for (int count = 0; count < total; ++count) std::lock_guard g (l); finish = std::chrono::high_resolution_clock::now (); }; //----------------------------------------------------------------------------- int main () { cruft::TAP::logger tap; cruft::thread::ticketlock l; l.lock (); tap.expect (true, "locked without contention"); l.unlock (); tap.expect (true, "unlocked without contention"); if (true || std::thread::hardware_concurrency () < 2) { tap.skip ("reasonably fair under contention"); } else { // measure fairness under contention is below some threshold // // * create two threads that will fight over a lock // * measure the start time // * fire an event to start the threads // * record the time the threads finish // * compare the difference in runtimes as a percentage // // we have to be reasonably generous with our test at the end because // there's liable to be a lot of noise in the measurement with a test // that runs in a short enough time period. constexpr int iterations = 1 << 16; cruft::thread::flag start_flag; ffs_t::time_point a_finish, b_finish; std::thread a ( fight, std::ref (start_flag), std::ref (l), iterations, std::ref (a_finish) ); std::thread b ( fight, std::ref (start_flag), std::ref (l), iterations, std::ref (b_finish) ); auto start = std::chrono::high_resolution_clock::now (); start_flag.notify_all (); a.join (); b.join (); auto a_total = std::chrono::duration_cast (a_finish - start).count (); auto b_total = std::chrono::duration_cast (b_finish - start).count (); auto diff = cruft::max (a_total, b_total) / float (cruft::min (a_total, b_total)); auto rel = cruft::abs (1 - diff); tap.expect_lt (rel, 0.1f, "reasonably fair under contention"); } return tap.status (); }