]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #pragma once | |
5 | ||
6 | #include <seastar/core/future.hh> | |
7 | #include <seastar/core/circular_buffer.hh> | |
8 | ||
9 | class read_lock { | |
10 | public: | |
11 | seastar::future<> lock(); | |
12 | void unlock(); | |
13 | }; | |
14 | ||
15 | class write_lock { | |
16 | public: | |
17 | seastar::future<> lock(); | |
18 | void unlock(); | |
19 | }; | |
20 | ||
21 | class excl_lock { | |
22 | public: | |
23 | seastar::future<> lock(); | |
24 | void unlock(); | |
25 | }; | |
26 | ||
27 | // promote from read to excl | |
28 | class excl_lock_from_read { | |
29 | public: | |
30 | seastar::future<> lock(); | |
31 | void unlock(); | |
32 | }; | |
33 | ||
34 | // promote from write to excl | |
35 | class excl_lock_from_write { | |
36 | public: | |
37 | seastar::future<> lock(); | |
38 | void unlock(); | |
39 | }; | |
40 | ||
41 | // promote from excl to excl | |
42 | class excl_lock_from_excl { | |
43 | public: | |
44 | seastar::future<> lock(); | |
45 | void unlock(); | |
46 | }; | |
47 | ||
48 | /// shared/exclusive mutual exclusion | |
49 | /// | |
50 | /// this lock design uses reader and writer is entirely and completely | |
51 | /// independent of the conventional reader/writer lock usage. Here, what we | |
52 | /// mean is that we can pipeline reads, and we can pipeline writes, but we | |
53 | /// cannot allow a read while writes are in progress or a write while reads are | |
54 | /// in progress. Any rmw operation is therefore exclusive. | |
55 | /// | |
56 | /// tri_mutex is based on seastar::shared_mutex, but instead of two kinds of | |
57 | /// waiters, tri_mutex keeps track of three kinds of lock users: | |
58 | /// - readers | |
59 | /// - writers | |
60 | /// - exclusive users | |
61 | class tri_mutex : private read_lock, | |
62 | write_lock, | |
63 | excl_lock, | |
64 | excl_lock_from_read, | |
65 | excl_lock_from_write, | |
66 | excl_lock_from_excl | |
67 | { | |
68 | public: | |
69 | tri_mutex() = default; | |
70 | ~tri_mutex(); | |
71 | ||
72 | read_lock& for_read() { | |
73 | return *this; | |
74 | } | |
75 | write_lock& for_write() { | |
76 | return *this; | |
77 | } | |
78 | excl_lock& for_excl() { | |
79 | return *this; | |
80 | } | |
81 | excl_lock_from_read& excl_from_read() { | |
82 | return *this; | |
83 | } | |
84 | excl_lock_from_write& excl_from_write() { | |
85 | return *this; | |
86 | } | |
87 | excl_lock_from_write& excl_from_excl() { | |
88 | return *this; | |
89 | } | |
90 | ||
91 | // for shared readers | |
92 | seastar::future<> lock_for_read(); | |
93 | bool try_lock_for_read() noexcept; | |
94 | void unlock_for_read(); | |
95 | void promote_from_read(); | |
96 | void demote_to_read(); | |
97 | unsigned get_readers() const { | |
98 | return readers; | |
99 | } | |
100 | ||
101 | // for shared writers | |
102 | seastar::future<> lock_for_write(bool greedy); | |
103 | bool try_lock_for_write(bool greedy) noexcept; | |
104 | void unlock_for_write(); | |
105 | void promote_from_write(); | |
106 | void demote_to_write(); | |
107 | unsigned get_writers() const { | |
108 | return writers; | |
109 | } | |
110 | ||
111 | // for exclusive users | |
112 | seastar::future<> lock_for_excl(); | |
113 | bool try_lock_for_excl() noexcept; | |
114 | void unlock_for_excl(); | |
115 | bool is_excl_acquired() const { | |
116 | return exclusively_used; | |
117 | } | |
118 | ||
119 | bool is_acquired() const; | |
120 | ||
121 | /// pass the provided exception to any waiting waiters | |
122 | template<typename Exception> | |
123 | void abort(Exception ex) { | |
124 | while (!waiters.empty()) { | |
125 | auto& waiter = waiters.front(); | |
126 | waiter.pr.set_exception(std::make_exception_ptr(ex)); | |
127 | waiters.pop_front(); | |
128 | } | |
129 | } | |
130 | ||
131 | private: | |
132 | void wake(); | |
133 | unsigned readers = 0; | |
134 | unsigned writers = 0; | |
135 | bool exclusively_used = false; | |
136 | enum class type_t : uint8_t { | |
137 | read, | |
138 | write, | |
139 | exclusive, | |
140 | none, | |
141 | }; | |
142 | struct waiter_t { | |
143 | waiter_t(seastar::promise<>&& pr, type_t type) | |
144 | : pr(std::move(pr)), type(type) | |
145 | {} | |
146 | seastar::promise<> pr; | |
147 | type_t type; | |
148 | }; | |
149 | seastar::circular_buffer<waiter_t> waiters; | |
150 | friend class read_lock; | |
151 | friend class write_lock; | |
152 | friend class excl_lock; | |
153 | friend class excl_lock_from_read; | |
154 | friend class excl_lock_from_write; | |
155 | friend class excl_lock_from_excl; | |
156 | }; |