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