2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <folly/IntrusiveList.h>
19 #include <folly/SpinLock.h>
20 #include <folly/fibers/GenericBaton.h>
28 * Like mutex but allows timed_lock in addition to lock and try_lock.
35 DCHECK(threadWaiters_.empty());
36 DCHECK(fiberWaiters_.empty());
37 DCHECK(notifiedFiber_ == nullptr);
40 TimedMutex(const TimedMutex& rhs) = delete;
41 TimedMutex& operator=(const TimedMutex& rhs) = delete;
42 TimedMutex(TimedMutex&& rhs) = delete;
43 TimedMutex& operator=(TimedMutex&& rhs) = delete;
45 // Lock the mutex. The thread / fiber is blocked until the mutex is free
48 // Lock the mutex. The thread / fiber will be blocked for a time duration.
50 // @return true if the mutex was locked, false otherwise
51 template <typename Rep, typename Period>
52 bool timed_lock(const std::chrono::duration<Rep, Period>& duration);
54 // Try to obtain lock without blocking the thread or fiber
57 // Unlock the mutex and wake up a waiter if there is one
61 enum class LockResult { SUCCESS, TIMEOUT, STOLEN };
63 template <typename WaitFunc>
64 LockResult lockHelper(WaitFunc&& waitFunc);
66 // represents a waiter waiting for the lock. The waiter waits on the
67 // baton until it is woken up by a post or timeout expires.
70 folly::IntrusiveListHook hook;
73 using MutexWaiterList = folly::IntrusiveList<MutexWaiter, &MutexWaiter::hook>;
75 folly::SpinLock lock_; //< lock to protect waiter list
76 bool locked_ = false; //< is this locked by some thread?
77 MutexWaiterList threadWaiters_; //< list of waiters
78 MutexWaiterList fiberWaiters_; //< list of waiters
79 MutexWaiter* notifiedFiber_{nullptr}; //< Fiber waiter which has been notified
85 * A readers-writer lock which allows multiple readers to hold the
86 * lock simultaneously or only one writer.
88 * NOTE: This is a reader-preferred RWLock i.e. readers are give priority
89 * when there are both readers and writers waiting to get the lock.
91 template <typename BatonType>
94 TimedRWMutex() = default;
95 ~TimedRWMutex() = default;
97 TimedRWMutex(const TimedRWMutex& rhs) = delete;
98 TimedRWMutex& operator=(const TimedRWMutex& rhs) = delete;
99 TimedRWMutex(TimedRWMutex&& rhs) = delete;
100 TimedRWMutex& operator=(TimedRWMutex&& rhs) = delete;
102 // Lock for shared access. The thread / fiber is blocked until the lock
106 // Like read_lock except the thread /fiber is blocked for a time duration
107 // @return true if locked successfully, false otherwise.
108 template <typename Rep, typename Period>
109 bool timed_read_lock(const std::chrono::duration<Rep, Period>& duration);
111 // Like read_lock but doesn't block the thread / fiber if the lock can't
113 // @return true if lock was acquired, false otherwise.
114 bool try_read_lock();
116 // Obtain an exclusive lock. The thread / fiber is blocked until the lock
120 // Like write_lock except the thread / fiber is blocked for a time duration
121 // @return true if locked successfully, false otherwise.
122 template <typename Rep, typename Period>
123 bool timed_write_lock(const std::chrono::duration<Rep, Period>& duration);
125 // Like write_lock but doesn't block the thread / fiber if the lock cant be
127 // @return true if lock was acquired, false otherwise.
128 bool try_write_lock();
130 // Wrapper for write_lock() for compatibility with Mutex
135 // Realease the lock. The thread / fiber will wake up all readers if there are
136 // any. If there are waiting writers then only one of them will be woken up.
137 // NOTE: readers are given priority over writers (see above comment)
140 // Downgrade the lock. The thread / fiber will wake up all readers if there
146 explicit ReadHolder(TimedRWMutex& lock) : lock_(&lock) {
156 ReadHolder(const ReadHolder& rhs) = delete;
157 ReadHolder& operator=(const ReadHolder& rhs) = delete;
158 ReadHolder(ReadHolder&& rhs) = delete;
159 ReadHolder& operator=(ReadHolder&& rhs) = delete;
167 explicit WriteHolder(TimedRWMutex& lock) : lock_(&lock) {
177 WriteHolder(const WriteHolder& rhs) = delete;
178 WriteHolder& operator=(const WriteHolder& rhs) = delete;
179 WriteHolder(WriteHolder&& rhs) = delete;
180 WriteHolder& operator=(WriteHolder&& rhs) = delete;
187 // invariants that must hold when the lock is not held by anyone
188 void verify_unlocked_properties() {
189 assert(readers_ == 0);
190 assert(read_waiters_.empty());
191 assert(write_waiters_.empty());
194 // Different states the lock can be in
201 typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
203 // represents a waiter waiting for the lock.
206 MutexWaiterHookType hook;
209 typedef boost::intrusive::
210 member_hook<MutexWaiter, MutexWaiterHookType, &MutexWaiter::hook>
213 typedef boost::intrusive::list<
216 boost::intrusive::constant_time_size<true>>
219 folly::SpinLock lock_; //< lock protecting the internal state
220 // (state_, read_waiters_, etc.)
221 State state_ = State::UNLOCKED;
223 uint32_t readers_ = 0; //< Number of readers who have the lock
225 MutexWaiterList write_waiters_; //< List of thread / fibers waiting for
228 MutexWaiterList read_waiters_; //< List of thread / fibers waiting for
231 } // namespace fibers
234 #include "TimedMutex-inl.h"