Seastar
High performance C++ framework for concurrent servers
shared_mutex.hh
1 /*
2  * This file is open source software, licensed to you under the terms
3  * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4  * distributed with this work for additional information regarding copyright
5  * ownership. You may not use this file except in compliance with the License.
6  *
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an
13  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  * KIND, either express or implied. See the License for the
15  * specific language governing permissions and limitations
16  * under the License.
17  */
18 /*
19  * Copyright (C) 2015 Cloudius Systems, Ltd.
20  */
21 
22 #pragma once
23 
24 #ifndef SEASTAR_MODULE
25 #include <seastar/core/future.hh>
26 #include <seastar/core/chunked_fifo.hh>
27 #include <seastar/util/modules.hh>
28 #include <cassert>
29 #include <utility>
30 #endif
31 
32 namespace seastar {
33 
34 SEASTAR_MODULE_EXPORT_BEGIN
35 
38 
56 class shared_mutex {
57  unsigned _readers = 0;
58  bool _writer = false;
59  struct waiter {
60  waiter(promise<>&& pr, bool for_write) : pr(std::move(pr)), for_write(for_write) {}
61  promise<> pr;
62  bool for_write;
63  };
64  chunked_fifo<waiter> _waiters;
65 public:
66  shared_mutex() = default;
67  shared_mutex(shared_mutex&&) = default;
68  shared_mutex& operator=(shared_mutex&&) = default;
69  shared_mutex(const shared_mutex&) = delete;
70  void operator=(const shared_mutex&) = delete;
75  future<> lock_shared() noexcept {
76  if (try_lock_shared()) {
77  return make_ready_future<>();
78  }
79  try {
80  _waiters.emplace_back(promise<>(), false);
81  return _waiters.back().pr.get_future();
82  } catch (...) {
84  }
85  }
89  bool try_lock_shared() noexcept {
90  if (!_writer && _waiters.empty()) {
91  ++_readers;
92  return true;
93  }
94  return false;
95  }
97  void unlock_shared() noexcept {
98  assert(_readers > 0);
99  --_readers;
100  wake();
101  }
106  future<> lock() noexcept {
107  if (try_lock()) {
108  return make_ready_future<>();
109  }
110  try {
111  _waiters.emplace_back(promise<>(), true);
112  return _waiters.back().pr.get_future();
113  } catch (...) {
115  }
116  }
120  bool try_lock() noexcept {
121  if (!_readers && !_writer) {
122  _writer = true;
123  return true;
124  }
125  return false;
126  }
128  void unlock() noexcept {
129  assert(_writer);
130  _writer = false;
131  wake();
132  }
133 private:
134  void wake() noexcept {
135  while (!_waiters.empty()) {
136  auto& w = _waiters.front();
137  // note: _writer == false in wake()
138  if (w.for_write) {
139  if (!_readers) {
140  _writer = true;
141  w.pr.set_value();
142  _waiters.pop_front();
143  }
144  break;
145  } else { // for read
146  ++_readers;
147  w.pr.set_value();
148  _waiters.pop_front();
149  }
150  }
151  }
152 };
153 
164 template <typename Func>
165 SEASTAR_CONCEPT(
166  requires (std::invocable<Func> && std::is_nothrow_move_constructible_v<Func>)
167  inline
168  futurize_t<std::invoke_result_t<Func>>
169 )
170 SEASTAR_NO_CONCEPT(
171  inline
172  std::enable_if_t<std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
173 )
174 with_shared(shared_mutex& sm, Func&& func) noexcept {
175  return sm.lock_shared().then([&sm, func = std::forward<Func>(func)] () mutable {
176  return futurize_invoke(func).finally([&sm] {
177  sm.unlock_shared();
178  });
179  });
180 }
181 
182 template <typename Func>
183 SEASTAR_CONCEPT(
184  requires (std::invocable<Func> && !std::is_nothrow_move_constructible_v<Func>)
185  inline
186  futurize_t<std::invoke_result_t<Func>>
187 )
188 SEASTAR_NO_CONCEPT(
189  inline
190  std::enable_if_t<!std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
191 )
192 with_shared(shared_mutex& sm, Func&& func) noexcept {
193  // FIXME: use a coroutine when c++17 support is dropped
194  try {
195  return do_with(std::forward<Func>(func), [&sm] (Func& func) {
196  return sm.lock_shared().then([&func] {
197  return func();
198  }).finally([&sm] {
199  sm.unlock_shared();
200  });
201  });
202  } catch (...) {
203  return futurize<std::invoke_result_t<Func>>::current_exception_as_future();
204  }
205 }
206 
217 template <typename Func>
218 SEASTAR_CONCEPT(
219  requires (std::invocable<Func> && std::is_nothrow_move_constructible_v<Func>)
220  inline
221  futurize_t<std::invoke_result_t<Func>>
222 )
223 SEASTAR_NO_CONCEPT(
224  inline
225  std::enable_if_t<std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
226 )
227 with_lock(shared_mutex& sm, Func&& func) noexcept {
228  return sm.lock().then([&sm, func = std::forward<Func>(func)] () mutable {
229  return futurize_invoke(func).finally([&sm] {
230  sm.unlock();
231  });
232  });
233 }
234 
235 
236 template <typename Func>
237 SEASTAR_CONCEPT(
238  requires (std::invocable<Func> && !std::is_nothrow_move_constructible_v<Func>)
239  inline
240  futurize_t<std::invoke_result_t<Func>>
241 )
242 SEASTAR_NO_CONCEPT(
243  inline
244  std::enable_if_t<!std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
245 )
246 with_lock(shared_mutex& sm, Func&& func) noexcept {
247  // FIXME: use a coroutine when c++17 support is dropped
248  try {
249  return do_with(std::forward<Func>(func), [&sm] (Func& func) {
250  return sm.lock().then([&func] {
251  return func();
252  }).finally([&sm] {
253  sm.unlock();
254  });
255  });
256  } catch (...) {
257  return futurize<std::invoke_result_t<Func>>::current_exception_as_future();
258  }
259 }
260 
262 SEASTAR_MODULE_EXPORT_END
263 
264 }
Shared/exclusive mutual exclusion.
Definition: shared_mutex.hh:56
void unlock_shared() noexcept
Unlocks a shared_mutex after a previous call to lock_shared().
Definition: shared_mutex.hh:97
future lock() noexcept
Definition: shared_mutex.hh:106
bool try_lock() noexcept
Definition: shared_mutex.hh:120
void unlock() noexcept
Unlocks a shared_mutex after a previous call to lock().
Definition: shared_mutex.hh:128
bool try_lock_shared() noexcept
Definition: shared_mutex.hh:89
future lock_shared() noexcept
Definition: shared_mutex.hh:75
future< T > current_exception_as_future() noexcept
Returns std::current_exception() wrapped in a future.
Definition: future.hh:1953
auto do_with(T1 &&rv1, T2 &&rv2, More &&... more) noexcept
Definition: do_with.hh:135
auto with_lock(Lock &lock, Func &&func)
Definition: do_with.hh:149
Seastar API namespace.
Definition: abort_on_ebadf.hh:26