summaryrefslogtreecommitdiffstats
path: root/src/test/common/test_fair_mutex.cc
blob: 10ba835a2ddb4b9479e8a152bda258712231c057 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-

#include <array>
#include <mutex>
#include <numeric>
#include <future>
#include <gtest/gtest.h>
#include "common/fair_mutex.h"

TEST(FairMutex, simple)
{
  ceph::fair_mutex mutex{"fair::simple"};
  {
    std::unique_lock lock{mutex};
    ASSERT_TRUE(mutex.is_locked());
    // fair_mutex does not recursive ownership semantics
    ASSERT_FALSE(mutex.try_lock());
  }
  // re-acquire the lock
  {
    std::unique_lock lock{mutex};
    ASSERT_TRUE(mutex.is_locked());
  }
  ASSERT_FALSE(mutex.is_locked());
}

TEST(FairMutex, fair)
{
  // waiters are queued in FIFO order, and they are woken up in the same order
  // we have a marathon participated by multiple teams:
  // - each team is represented by a thread.
  // - each team should have equal chance of being selected and scoring, assuming
  //   the runners in each team are distributed evenly in the waiting queue.
  ceph::fair_mutex mutex{"fair::fair"};
  const int NR_TEAMS = 2;
  std::array<unsigned, NR_TEAMS> scoreboard{0, 0};
  const int NR_ROUNDS = 512;
  auto play = [&](int team) {
    for (int i = 0; i < NR_ROUNDS; i++) {
      std::unique_lock lock{mutex};
      // pretent that i am running.. and it takes time
      std::this_thread::sleep_for(std::chrono::microseconds(20));
      // score!
      scoreboard[team]++;
      // fair?
      unsigned total = std::accumulate(scoreboard.begin(),
                                       scoreboard.end(),
                                       0);
      for (unsigned score : scoreboard) {
        if (total < NR_ROUNDS) {
          // not quite statistically significant. to reduce the false positive,
          // just consider it fair
          continue;
        }
        // check if any team is donimating the game.
        unsigned avg = total / scoreboard.size();
        // leave at least half of the average to other teams
        ASSERT_LE(score, total - avg / 2);
        // don't treat myself too bad
        ASSERT_GT(score, avg / 2);
      };
    }
  };
  std::array<std::future<void>, NR_TEAMS> completed;
  for (int team = 0; team < NR_TEAMS; team++) {
    completed[team] = std::async(std::launch::async, play, team);
  }
}