summaryrefslogtreecommitdiffstats
path: root/src/crimson/common/tri_mutex.h
blob: 0533f3539d970d4067ed4cfafd0a00a92d8dd665 (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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
// vim: ts=8 sw=2 smarttab

#pragma once

#include <seastar/core/future.hh>
#include <seastar/core/circular_buffer.hh>

class read_lock {
public:
  seastar::future<> lock();
  void unlock();
};

class write_lock {
public:
  seastar::future<> lock();
  void unlock();
};

class excl_lock {
public:
  seastar::future<> lock();
  void unlock();
};

// promote from read to excl
class excl_lock_from_read {
public:
  seastar::future<> lock();
  void unlock();
};

// promote from write to excl
class excl_lock_from_write {
public:
  seastar::future<> lock();
  void unlock();
};

// promote from excl to excl
class excl_lock_from_excl {
public:
  seastar::future<> lock();
  void unlock();
};

/// shared/exclusive mutual exclusion
///
/// this lock design uses reader and writer is entirely and completely
/// independent of the conventional reader/writer lock usage. Here, what we
/// mean is that we can pipeline reads, and we can pipeline writes, but we
/// cannot allow a read while writes are in progress or a write while reads are
/// in progress. Any rmw operation is therefore exclusive.
///
/// tri_mutex is based on seastar::shared_mutex, but instead of two kinds of
/// waiters, tri_mutex keeps track of three kinds of lock users:
/// - readers
/// - writers
/// - exclusive users
class tri_mutex : private read_lock,
                          write_lock,
                          excl_lock,
                          excl_lock_from_read,
                          excl_lock_from_write,
                          excl_lock_from_excl
{
public:
  tri_mutex() = default;
  ~tri_mutex();

  read_lock& for_read() {
    return *this;
  }
  write_lock& for_write() {
    return *this;
  }
  excl_lock& for_excl() {
    return *this;
  }
  excl_lock_from_read& excl_from_read() {
    return *this;
  }
  excl_lock_from_write& excl_from_write() {
    return *this;
  }
  excl_lock_from_excl& excl_from_excl() {
    return *this;
  }

  // for shared readers
  seastar::future<> lock_for_read();
  bool try_lock_for_read() noexcept;
  void unlock_for_read();
  void promote_from_read();
  void demote_to_read();
  unsigned get_readers() const {
    return readers;
  }

  // for shared writers
  seastar::future<> lock_for_write(bool greedy);
  bool try_lock_for_write(bool greedy) noexcept;
  void unlock_for_write();
  void promote_from_write();
  void demote_to_write();
  unsigned get_writers() const {
    return writers;
  }

  // for exclusive users
  seastar::future<> lock_for_excl();
  bool try_lock_for_excl() noexcept;
  void unlock_for_excl();
  bool is_excl_acquired() const {
    return exclusively_used;
  }

  bool is_acquired() const;

  /// pass the provided exception to any waiting waiters
  template<typename Exception>
  void abort(Exception ex) {
    while (!waiters.empty()) {
      auto& waiter = waiters.front();
      waiter.pr.set_exception(std::make_exception_ptr(ex));
      waiters.pop_front();
    }
  }

private:
  void wake();
  unsigned readers = 0;
  unsigned writers = 0;
  bool exclusively_used = false;
  enum class type_t : uint8_t {
    read,
    write,
    exclusive,
    none,
  };
  struct waiter_t {
    waiter_t(seastar::promise<>&& pr, type_t type)
      : pr(std::move(pr)), type(type)
    {}
    seastar::promise<> pr;
    type_t type;
  };
  seastar::circular_buffer<waiter_t> waiters;
  friend class read_lock;
  friend class write_lock;
  friend class excl_lock;
  friend class excl_lock_from_read;
  friend class excl_lock_from_write;
  friend class excl_lock_from_excl;
};