]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/shared_mutex.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / core / shared_mutex.hh
CommitLineData
11fdf7f2
TL
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#include <seastar/core/future.hh>
20effc67 25#include <seastar/core/chunked_fifo.hh>
11fdf7f2
TL
26
27namespace seastar {
28
29/// \addtogroup fiber-module
30/// @{
31
32/// \brief Shared/exclusive mutual exclusion.
33///
34/// Similar to \c std::shared_mutex, this class provides protection
35/// for a shared resource, with two levels of access protection: shared
36/// and exclusive. Shared access allows multiple tasks to access the
37/// shared resource concurrently, while exclusive access allows just
38/// one task to access the resource at a time.
39///
40/// Note that many seastar tasks do not require protection at all,
41/// since the seastar scheduler is not preemptive; however tasks that do
42/// (by waiting on a future) may require explicit locking.
43///
44/// The \ref with_shared(shared_mutex&, Func&&) and
45/// \ref with_lock(shared_mutex&, Func&&) provide exception-safe
46/// wrappers for use with \c shared_mutex.
47///
48/// \see semaphore simpler mutual exclusion
49class shared_mutex {
50 unsigned _readers = 0;
51 bool _writer = false;
52 struct waiter {
53 waiter(promise<>&& pr, bool for_write) : pr(std::move(pr)), for_write(for_write) {}
54 promise<> pr;
55 bool for_write;
56 };
20effc67 57 chunked_fifo<waiter> _waiters;
11fdf7f2
TL
58public:
59 shared_mutex() = default;
60 shared_mutex(shared_mutex&&) = default;
61 shared_mutex& operator=(shared_mutex&&) = default;
62 shared_mutex(const shared_mutex&) = delete;
63 void operator=(const shared_mutex&) = delete;
64 /// Lock the \c shared_mutex for shared access
65 ///
66 /// \return a future that becomes ready when no exclusive access
67 /// is granted to anyone.
20effc67 68 future<> lock_shared() noexcept {
f67539c2 69 if (try_lock_shared()) {
11fdf7f2
TL
70 return make_ready_future<>();
71 }
20effc67
TL
72 try {
73 _waiters.emplace_back(promise<>(), false);
74 return _waiters.back().pr.get_future();
75 } catch (...) {
76 return current_exception_as_future();
77 }
11fdf7f2 78 }
f67539c2
TL
79 /// Try to lock the \c shared_mutex for shared access
80 ///
81 /// \return true iff could acquire the lock for shared access.
82 bool try_lock_shared() noexcept {
83 if (!_writer && _waiters.empty()) {
84 ++_readers;
85 return true;
86 }
87 return false;
88 }
11fdf7f2 89 /// Unlocks a \c shared_mutex after a previous call to \ref lock_shared().
20effc67 90 void unlock_shared() noexcept {
f67539c2 91 assert(_readers > 0);
11fdf7f2
TL
92 --_readers;
93 wake();
94 }
95 /// Lock the \c shared_mutex for exclusive access
96 ///
97 /// \return a future that becomes ready when no access, shared or exclusive
98 /// is granted to anyone.
20effc67 99 future<> lock() noexcept {
f67539c2 100 if (try_lock()) {
11fdf7f2
TL
101 return make_ready_future<>();
102 }
20effc67
TL
103 try {
104 _waiters.emplace_back(promise<>(), true);
105 return _waiters.back().pr.get_future();
106 } catch (...) {
107 return current_exception_as_future();
108 }
11fdf7f2 109 }
f67539c2
TL
110 /// Try to lock the \c shared_mutex for exclusive access
111 ///
112 /// \return true iff could acquire the lock for exclusive access.
113 bool try_lock() noexcept {
114 if (!_readers && !_writer) {
115 _writer = true;
116 return true;
117 }
118 return false;
119 }
11fdf7f2 120 /// Unlocks a \c shared_mutex after a previous call to \ref lock().
20effc67 121 void unlock() noexcept {
f67539c2 122 assert(_writer);
11fdf7f2
TL
123 _writer = false;
124 wake();
125 }
126private:
20effc67 127 void wake() noexcept {
11fdf7f2
TL
128 while (!_waiters.empty()) {
129 auto& w = _waiters.front();
130 // note: _writer == false in wake()
131 if (w.for_write) {
132 if (!_readers) {
133 _writer = true;
134 w.pr.set_value();
135 _waiters.pop_front();
136 }
137 break;
138 } else { // for read
139 ++_readers;
140 w.pr.set_value();
141 _waiters.pop_front();
142 }
143 }
144 }
145};
146
147/// Executes a function while holding shared access to a resource.
148///
149/// Executes a function while holding shared access to a resource. When
150/// the function returns, the mutex is automatically unlocked.
151///
152/// \param sm a \ref shared_mutex guarding access to the shared resource
153/// \param func callable object to invoke while the mutex is held for shared access
154/// \return whatever \c func returns, as a future
155///
156/// \relates shared_mutex
157template <typename Func>
20effc67
TL
158SEASTAR_CONCEPT(
159 requires (std::invocable<Func> && std::is_nothrow_move_constructible_v<Func>)
160 inline
161 futurize_t<std::invoke_result_t<Func>>
162)
163SEASTAR_NO_CONCEPT(
164 inline
165 std::enable_if_t<std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
166)
167with_shared(shared_mutex& sm, Func&& func) noexcept {
f67539c2
TL
168 return sm.lock_shared().then([&sm, func = std::forward<Func>(func)] () mutable {
169 return futurize_invoke(func).finally([&sm] {
170 sm.unlock_shared();
171 });
11fdf7f2
TL
172 });
173}
174
20effc67
TL
175template <typename Func>
176SEASTAR_CONCEPT(
177 requires (std::invocable<Func> && !std::is_nothrow_move_constructible_v<Func>)
178 inline
179 futurize_t<std::invoke_result_t<Func>>
180)
181SEASTAR_NO_CONCEPT(
182 inline
183 std::enable_if_t<!std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
184)
185with_shared(shared_mutex& sm, Func&& func) noexcept {
186 // FIXME: use a coroutine when c++17 support is dropped
187 try {
188 return do_with(std::forward<Func>(func), [&sm] (Func& func) {
189 return sm.lock_shared().then([&func] {
190 return func();
191 }).finally([&sm] {
192 sm.unlock_shared();
193 });
194 });
195 } catch (...) {
1e59de90 196 return futurize<std::invoke_result_t<Func>>::current_exception_as_future();
20effc67
TL
197 }
198}
199
11fdf7f2
TL
200/// Executes a function while holding exclusive access to a resource.
201///
202/// Executes a function while holding exclusive access to a resource. When
203/// the function returns, the mutex is automatically unlocked.
204///
205/// \param sm a \ref shared_mutex guarding access to the shared resource
206/// \param func callable object to invoke while the mutex is held for shared access
207/// \return whatever \c func returns, as a future
208///
209/// \relates shared_mutex
210template <typename Func>
20effc67
TL
211SEASTAR_CONCEPT(
212 requires (std::invocable<Func> && std::is_nothrow_move_constructible_v<Func>)
213 inline
214 futurize_t<std::invoke_result_t<Func>>
215)
216SEASTAR_NO_CONCEPT(
217 inline
218 std::enable_if_t<std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
219)
220with_lock(shared_mutex& sm, Func&& func) noexcept {
f67539c2
TL
221 return sm.lock().then([&sm, func = std::forward<Func>(func)] () mutable {
222 return futurize_invoke(func).finally([&sm] {
223 sm.unlock();
224 });
11fdf7f2
TL
225 });
226}
227
20effc67
TL
228
229template <typename Func>
230SEASTAR_CONCEPT(
231 requires (std::invocable<Func> && !std::is_nothrow_move_constructible_v<Func>)
232 inline
233 futurize_t<std::invoke_result_t<Func>>
234)
235SEASTAR_NO_CONCEPT(
236 inline
237 std::enable_if_t<!std::is_nothrow_move_constructible_v<Func>, futurize_t<std::result_of_t<Func ()>>>
238)
239with_lock(shared_mutex& sm, Func&& func) noexcept {
240 // FIXME: use a coroutine when c++17 support is dropped
241 try {
242 return do_with(std::forward<Func>(func), [&sm] (Func& func) {
243 return sm.lock().then([&func] {
244 return func();
245 }).finally([&sm] {
246 sm.unlock();
247 });
248 });
249 } catch (...) {
1e59de90 250 return futurize<std::invoke_result_t<Func>>::current_exception_as_future();
20effc67
TL
251 }
252}
253
11fdf7f2
TL
254/// @}
255
256}