summaryrefslogtreecommitdiffstats
path: root/src/seastar/include/seastar/core/rwlock.hh
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/seastar/include/seastar/core/rwlock.hh180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/seastar/include/seastar/core/rwlock.hh b/src/seastar/include/seastar/core/rwlock.hh
new file mode 100644
index 000000000..58b046a2f
--- /dev/null
+++ b/src/seastar/include/seastar/core/rwlock.hh
@@ -0,0 +1,180 @@
+/*
+* 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/semaphore.hh>
+
+namespace seastar {
+
+/// \cond internal
+// lock / unlock semantics for rwlock, so it can be used with with_lock()
+template<typename Clock>
+class basic_rwlock;
+
+template<typename Clock = typename timer<>::clock>
+class rwlock_for_read {
+public:
+ future<> lock() {
+ return static_cast<basic_rwlock<Clock>*>(this)->read_lock();
+ }
+ void unlock() {
+ static_cast<basic_rwlock<Clock>*>(this)->read_unlock();
+ }
+ friend class basic_rwlock<Clock>;
+};
+
+template<typename Clock = typename timer<>::clock>
+class rwlock_for_write {
+public:
+ future<> lock() {
+ return static_cast<basic_rwlock<Clock>*>(this)->write_lock();
+ }
+ void unlock() {
+ static_cast<basic_rwlock<Clock>*>(this)->write_unlock();
+ }
+ friend class basic_rwlock<Clock>;
+};
+/// \endcond
+
+
+/// \addtogroup fiber-module
+/// @{
+
+/// Implements a read-write lock mechanism. Beware: this is not a cross-CPU
+/// lock, due to seastar's sharded architecture.
+/// Instead, it can be used to achieve rwlock semantics between two (or more)
+/// fibers running in the same CPU that may use the same resource.
+/// Acquiring the write lock will effectively cause all readers not to be executed
+/// until the write part is done.
+template<typename Clock = typename timer<>::clock>
+class basic_rwlock : private rwlock_for_read<Clock>, rwlock_for_write<Clock> {
+ using semaphore_type = basic_semaphore<semaphore_default_exception_factory, Clock>;
+
+ static constexpr size_t max_ops = semaphore_type::max_counter();
+
+ semaphore_type _sem;
+public:
+ basic_rwlock()
+ : _sem(max_ops) {
+ }
+
+ /// Cast this rwlock into read lock object with lock semantics appropriate to be used
+ /// by "with_lock". The resulting object will have lock / unlock calls that, when called,
+ /// will acquire / release the lock in read mode.
+ rwlock_for_read<Clock>& for_read() {
+ return *this;
+ }
+
+ /// Cast this rwlock into write lock object with lock semantics appropriate to be used
+ /// by "with_lock". The resulting object will have lock / unlock calls that, when called,
+ /// will acquire / release the lock in write mode.
+ rwlock_for_write<Clock>& for_write() {
+ return *this;
+ }
+
+ /// Acquires this lock in read mode. Many readers are allowed, but when
+ /// this future returns, and until \ref read_unlock is called, all fibers
+ /// waiting on \ref write_lock are guaranteed not to execute.
+ future<> read_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
+ return _sem.wait(timeout);
+ }
+
+ /// Releases the lock, which must have been taken in read mode. After this
+ /// is called, one of the fibers waiting on \ref write_lock will be allowed
+ /// to proceed.
+ void read_unlock() {
+ assert(_sem.current() < max_ops);
+ _sem.signal();
+ }
+
+ /// Acquires this lock in write mode. Only one writer is allowed. When
+ /// this future returns, and until \ref write_unlock is called, all other
+ /// fibers waiting on either \ref read_lock or \ref write_lock are guaranteed
+ /// not to execute.
+ future<> write_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
+ return _sem.wait(timeout, max_ops);
+ }
+
+ /// Releases the lock, which must have been taken in write mode. After this
+ /// is called, one of the other fibers waiting on \ref write_lock or the fibers
+ /// waiting on \ref read_lock will be allowed to proceed.
+ void write_unlock() {
+ assert(_sem.current() == 0);
+ _sem.signal(max_ops);
+ }
+
+ /// Tries to acquire the lock in read mode iff this can be done without waiting.
+ bool try_read_lock() {
+ return _sem.try_wait();
+ }
+
+ /// Tries to acquire the lock in write mode iff this can be done without waiting.
+ bool try_write_lock() {
+ return _sem.try_wait(max_ops);
+ }
+
+ using holder = semaphore_units<semaphore_default_exception_factory, Clock>;
+
+ /// hold_read_lock() waits for a read lock and returns an object which,
+ /// when destroyed, releases the lock. This makes it easy to ensure that
+ /// the lock is eventually undone, at any circumstance (even including
+ /// exceptions). The release() method can be used on the returned object
+ /// to release its ownership of the lock and avoid the automatic unlock.
+ /// Note that both hold_read_lock() and hold_write_lock() return an object
+ /// of the same type, rwlock::holder.
+ ///
+ /// hold_read_lock() may throw an exception (or, in other implementations,
+ /// return an exceptional future) when it failed to obtain the lock -
+ /// e.g., on allocation failure.
+ future<holder> hold_read_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
+ return get_units(_sem, 1);
+ }
+
+ /// hold_write_lock() waits for a write lock and returns an object which,
+ /// when destroyed, releases the lock. This makes it easy to ensure that
+ /// the lock is eventually undone, at any circumstance (even including
+ /// exceptions). The release() method can be used on the returned object
+ /// to release its ownership of the lock and avoid the automatic unlock.
+ /// Note that both hold_read_lock() and hold_write_lock() return an object
+ /// of the same type, rwlock::holder.
+ ///
+ /// hold_read_lock() may throw an exception (or, in other implementations,
+ /// return an exceptional future) when it failed to obtain the lock -
+ /// e.g., on allocation failure.
+ future<holder> hold_write_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
+ return get_units(_sem, max_ops);
+ }
+
+ /// Checks if any read or write locks are currently held.
+ bool locked() const {
+ return _sem.available_units() != max_ops;
+ }
+
+ friend class rwlock_for_read<Clock>;
+ friend class rwlock_for_write<Clock>;
+};
+
+using rwlock = basic_rwlock<>;
+
+/// @}
+
+}