]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/rwlock.hh
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / seastar / include / seastar / core / rwlock.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/semaphore.hh>
25
26namespace seastar {
27
28/// \cond internal
29// lock / unlock semantics for rwlock, so it can be used with with_lock()
30template<typename Clock>
31class basic_rwlock;
32
33template<typename Clock = typename timer<>::clock>
34struct rwlock_for_read {
35 future<> lock() {
36 return static_cast<basic_rwlock<Clock>*>(this)->read_lock();
37 }
38 void unlock() {
39 static_cast<basic_rwlock<Clock>*>(this)->read_unlock();
40 }
41 friend class basic_rwlock<Clock>;
42};
43
44template<typename Clock = typename timer<>::clock>
45struct rwlock_for_write {
46 future<> lock() {
47 return static_cast<basic_rwlock<Clock>*>(this)->write_lock();
48 }
49 void unlock() {
50 static_cast<basic_rwlock<Clock>*>(this)->write_unlock();
51 }
52 friend class basic_rwlock<Clock>;
53};
54/// \endcond
55
56
57/// \addtogroup fiber-module
58/// @{
59
60/// Implements a read-write lock mechanism. Beware: this is not a cross-CPU
61/// lock, due to seastar's sharded architecture.
62/// Instead, it can be used to achieve rwlock semantics between two (or more)
63/// fibers running in the same CPU that may use the same resource.
64/// Acquiring the write lock will effectively cause all readers not to be executed
65/// until the write part is done.
66template<typename Clock = typename timer<>::clock>
67class basic_rwlock : private rwlock_for_read<Clock>, rwlock_for_write<Clock> {
68 using semaphore_type = basic_semaphore<semaphore_default_exception_factory, Clock>;
69
70 static constexpr size_t max_ops = semaphore_type::max_counter();
71
72 semaphore_type _sem;
73public:
74 basic_rwlock()
75 : _sem(max_ops) {
76 }
77
78 /// Cast this rwlock into read lock object with lock semantics appropriate to be used
79 /// by "with_lock". The resulting object will have lock / unlock calls that, when called,
80 /// will acquire / release the lock in read mode.
81 rwlock_for_read<Clock>& for_read() {
82 return *this;
83 }
84
85 /// Cast this rwlock into write lock object with lock semantics appropriate to be used
86 /// by "with_lock". The resulting object will have lock / unlock calls that, when called,
87 /// will acquire / release the lock in write mode.
88 rwlock_for_write<Clock>& for_write() {
89 return *this;
90 }
91
92 /// Acquires this lock in read mode. Many readers are allowed, but when
93 /// this future returns, and until \ref read_unlock is called, all fibers
94 /// waiting on \ref write_lock are guaranteed not to execute.
95 future<> read_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
96 return _sem.wait(timeout);
97 }
98
99 /// Releases the lock, which must have been taken in read mode. After this
100 /// is called, one of the fibers waiting on \ref write_lock will be allowed
101 /// to proceed.
102 void read_unlock() {
103#ifdef SEASTAR_DEBUG_LOCKING
104 assert(_sem.current() < max_ops);
105#endif
106 _sem.signal();
107 }
108
109 /// Acquires this lock in write mode. Only one writer is allowed. When
110 /// this future returns, and until \ref write_unlock is called, all other
111 /// fibers waiting on either \ref read_lock or \ref write_lock are guaranteed
112 /// not to execute.
113 future<> write_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
114 return _sem.wait(timeout, max_ops);
115 }
116
117 /// Releases the lock, which must have been taken in write mode. After this
118 /// is called, one of the other fibers waiting on \ref write_lock or the fibers
119 /// waiting on \ref read_lock will be allowed to proceed.
120 void write_unlock() {
121#ifdef SEASTAR_DEBUG_LOCKING
122 assert(_sem.current() == 0);
123#endif
124 _sem.signal(max_ops);
125 }
126
127 /// Tries to acquire the lock in read mode iff this can be done without waiting.
128 bool try_read_lock() {
129 return _sem.try_wait();
130 }
131
132 /// Tries to acquire the lock in write mode iff this can be done without waiting.
133 bool try_write_lock() {
134 return _sem.try_wait(max_ops);
135 }
136
137 using holder = semaphore_units<semaphore_default_exception_factory, Clock>;
138
139 /// hold_read_lock() waits for a read lock and returns an object which,
140 /// when destroyed, releases the lock. This makes it easy to ensure that
141 /// the lock is eventually undone, at any circumstance (even including
142 /// exceptions). The release() method can be used on the returned object
143 /// to release its ownership of the lock and avoid the automatic unlock.
144 /// Note that both hold_read_lock() and hold_write_lock() return an object
145 /// of the same type, rwlock::holder.
146 ///
147 /// hold_read_lock() may throw an exception (or, in other implementations,
148 /// return an exceptional future) when it failed to obtain the lock -
149 /// e.g., on allocation failure.
150 future<holder> hold_read_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
151 return get_units(_sem, 1);
152 }
153
154 /// hold_write_lock() waits for a write lock and returns an object which,
155 /// when destroyed, releases the lock. This makes it easy to ensure that
156 /// the lock is eventually undone, at any circumstance (even including
157 /// exceptions). The release() method can be used on the returned object
158 /// to release its ownership of the lock and avoid the automatic unlock.
159 /// Note that both hold_read_lock() and hold_write_lock() return an object
160 /// of the same type, rwlock::holder.
161 ///
162 /// hold_read_lock() may throw an exception (or, in other implementations,
163 /// return an exceptional future) when it failed to obtain the lock -
164 /// e.g., on allocation failure.
165 future<holder> hold_write_lock(typename semaphore_type::time_point timeout = semaphore_type::time_point::max()) {
166 return get_units(_sem, max_ops);
167 }
168
169 /// Checks if any read or write locks are currently held.
170 bool locked() const {
171 return _sem.available_units() != max_ops;
172 }
173
174 friend class rwlock_for_read<Clock>;
175 friend class rwlock_for_write<Clock>;
176};
177
178using rwlock = basic_rwlock<>;
179
180/// @}
181
182}