]>
Commit | Line | Data |
---|---|---|
a4b75251 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- |
2 | ||
3 | #include <array> | |
4 | #include <mutex> | |
5 | #include <numeric> | |
6 | #include <future> | |
7 | #include <gtest/gtest.h> | |
8 | #include "common/fair_mutex.h" | |
9 | ||
10 | TEST(FairMutex, simple) | |
11 | { | |
12 | ceph::fair_mutex mutex{"fair::simple"}; | |
13 | { | |
14 | std::unique_lock lock{mutex}; | |
15 | ASSERT_TRUE(mutex.is_locked()); | |
16 | // fair_mutex does not recursive ownership semantics | |
17 | ASSERT_FALSE(mutex.try_lock()); | |
18 | } | |
19 | // re-acquire the lock | |
20 | { | |
21 | std::unique_lock lock{mutex}; | |
22 | ASSERT_TRUE(mutex.is_locked()); | |
23 | } | |
24 | ASSERT_FALSE(mutex.is_locked()); | |
25 | } | |
26 | ||
27 | TEST(FairMutex, fair) | |
28 | { | |
29 | // waiters are queued in FIFO order, and they are woken up in the same order | |
30 | // we have a marathon participated by multiple teams: | |
31 | // - each team is represented by a thread. | |
32 | // - each team should have equal chance of being selected and scoring, assuming | |
33 | // the runners in each team are distributed evenly in the waiting queue. | |
34 | ceph::fair_mutex mutex{"fair::fair"}; | |
35 | const int NR_TEAMS = 2; | |
36 | std::array<unsigned, NR_TEAMS> scoreboard{0, 0}; | |
37 | const int NR_ROUNDS = 512; | |
38 | auto play = [&](int team) { | |
39 | for (int i = 0; i < NR_ROUNDS; i++) { | |
40 | std::unique_lock lock{mutex}; | |
41 | // pretent that i am running.. and it takes time | |
42 | std::this_thread::sleep_for(std::chrono::microseconds(20)); | |
43 | // score! | |
44 | scoreboard[team]++; | |
45 | // fair? | |
46 | unsigned total = std::accumulate(scoreboard.begin(), | |
47 | scoreboard.end(), | |
48 | 0); | |
49 | for (unsigned score : scoreboard) { | |
50 | if (total < NR_ROUNDS) { | |
51 | // not quite statistically significant. to reduce the false positive, | |
52 | // just consider it fair | |
53 | continue; | |
54 | } | |
55 | // check if any team is donimating the game. | |
56 | unsigned avg = total / scoreboard.size(); | |
57 | // leave at least half of the average to other teams | |
58 | ASSERT_LE(score, total - avg / 2); | |
59 | // don't treat myself too bad | |
60 | ASSERT_GT(score, avg / 2); | |
61 | }; | |
62 | } | |
63 | }; | |
64 | std::array<std::future<void>, NR_TEAMS> completed; | |
65 | for (int team = 0; team < NR_TEAMS; team++) { | |
66 | completed[team] = std::async(std::launch::async, play, team); | |
67 | } | |
68 | } |