summaryrefslogtreecommitdiffstats
path: root/src/seastar/include/seastar/core/shared_mutex.hh
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/seastar/include/seastar/core/shared_mutex.hh183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/seastar/include/seastar/core/shared_mutex.hh b/src/seastar/include/seastar/core/shared_mutex.hh
new file mode 100644
index 000000000..553f47e74
--- /dev/null
+++ b/src/seastar/include/seastar/core/shared_mutex.hh
@@ -0,0 +1,183 @@
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2015 Cloudius Systems, Ltd.
+ */
+
+#pragma once
+
+#include <seastar/core/future.hh>
+#include <seastar/core/circular_buffer.hh>
+
+namespace seastar {
+
+/// \addtogroup fiber-module
+/// @{
+
+/// \brief Shared/exclusive mutual exclusion.
+///
+/// Similar to \c std::shared_mutex, this class provides protection
+/// for a shared resource, with two levels of access protection: shared
+/// and exclusive. Shared access allows multiple tasks to access the
+/// shared resource concurrently, while exclusive access allows just
+/// one task to access the resource at a time.
+///
+/// Note that many seastar tasks do not require protection at all,
+/// since the seastar scheduler is not preemptive; however tasks that do
+/// (by waiting on a future) may require explicit locking.
+///
+/// The \ref with_shared(shared_mutex&, Func&&) and
+/// \ref with_lock(shared_mutex&, Func&&) provide exception-safe
+/// wrappers for use with \c shared_mutex.
+///
+/// \see semaphore simpler mutual exclusion
+class shared_mutex {
+ unsigned _readers = 0;
+ bool _writer = false;
+ struct waiter {
+ waiter(promise<>&& pr, bool for_write) : pr(std::move(pr)), for_write(for_write) {}
+ promise<> pr;
+ bool for_write;
+ };
+ circular_buffer<waiter> _waiters;
+public:
+ shared_mutex() = default;
+ shared_mutex(shared_mutex&&) = default;
+ shared_mutex& operator=(shared_mutex&&) = default;
+ shared_mutex(const shared_mutex&) = delete;
+ void operator=(const shared_mutex&) = delete;
+ /// Lock the \c shared_mutex for shared access
+ ///
+ /// \return a future that becomes ready when no exclusive access
+ /// is granted to anyone.
+ future<> lock_shared() {
+ if (try_lock_shared()) {
+ return make_ready_future<>();
+ }
+ _waiters.emplace_back(promise<>(), false);
+ return _waiters.back().pr.get_future();
+ }
+ /// Try to lock the \c shared_mutex for shared access
+ ///
+ /// \return true iff could acquire the lock for shared access.
+ bool try_lock_shared() noexcept {
+ if (!_writer && _waiters.empty()) {
+ ++_readers;
+ return true;
+ }
+ return false;
+ }
+ /// Unlocks a \c shared_mutex after a previous call to \ref lock_shared().
+ void unlock_shared() {
+ assert(_readers > 0);
+ --_readers;
+ wake();
+ }
+ /// Lock the \c shared_mutex for exclusive access
+ ///
+ /// \return a future that becomes ready when no access, shared or exclusive
+ /// is granted to anyone.
+ future<> lock() {
+ if (try_lock()) {
+ return make_ready_future<>();
+ }
+ _waiters.emplace_back(promise<>(), true);
+ return _waiters.back().pr.get_future();
+ }
+ /// Try to lock the \c shared_mutex for exclusive access
+ ///
+ /// \return true iff could acquire the lock for exclusive access.
+ bool try_lock() noexcept {
+ if (!_readers && !_writer) {
+ _writer = true;
+ return true;
+ }
+ return false;
+ }
+ /// Unlocks a \c shared_mutex after a previous call to \ref lock().
+ void unlock() {
+ assert(_writer);
+ _writer = false;
+ wake();
+ }
+private:
+ void wake() {
+ while (!_waiters.empty()) {
+ auto& w = _waiters.front();
+ // note: _writer == false in wake()
+ if (w.for_write) {
+ if (!_readers) {
+ _writer = true;
+ w.pr.set_value();
+ _waiters.pop_front();
+ }
+ break;
+ } else { // for read
+ ++_readers;
+ w.pr.set_value();
+ _waiters.pop_front();
+ }
+ }
+ }
+};
+
+/// Executes a function while holding shared access to a resource.
+///
+/// Executes a function while holding shared access to a resource. When
+/// the function returns, the mutex is automatically unlocked.
+///
+/// \param sm a \ref shared_mutex guarding access to the shared resource
+/// \param func callable object to invoke while the mutex is held for shared access
+/// \return whatever \c func returns, as a future
+///
+/// \relates shared_mutex
+template <typename Func>
+inline
+futurize_t<std::result_of_t<Func ()>>
+with_shared(shared_mutex& sm, Func&& func) {
+ return sm.lock_shared().then([&sm, func = std::forward<Func>(func)] () mutable {
+ return futurize_invoke(func).finally([&sm] {
+ sm.unlock_shared();
+ });
+ });
+}
+
+/// Executes a function while holding exclusive access to a resource.
+///
+/// Executes a function while holding exclusive access to a resource. When
+/// the function returns, the mutex is automatically unlocked.
+///
+/// \param sm a \ref shared_mutex guarding access to the shared resource
+/// \param func callable object to invoke while the mutex is held for shared access
+/// \return whatever \c func returns, as a future
+///
+/// \relates shared_mutex
+template <typename Func>
+inline
+futurize_t<std::result_of_t<Func ()>>
+with_lock(shared_mutex& sm, Func&& func) {
+ return sm.lock().then([&sm, func = std::forward<Func>(func)] () mutable {
+ return futurize_invoke(func).finally([&sm] {
+ sm.unlock();
+ });
+ });
+}
+
+/// @}
+
+}