2 * Copyright 2015 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.
19 namespace folly { namespace fibers {
22 // TimedMutex implementation
25 template <typename BatonType>
26 void TimedMutex<BatonType>::lock() {
27 pthread_spin_lock(&lock_);
30 pthread_spin_unlock(&lock_);
34 // Delay constructing the waiter until it is actually required.
35 // This makes a huge difference, at least in the benchmarks,
36 // when the mutex isn't locked.
38 waiters_.push_back(waiter);
39 pthread_spin_unlock(&lock_);
43 template <typename BatonType>
44 template <typename Rep, typename Period>
45 bool TimedMutex<BatonType>::timed_lock(
46 const std::chrono::duration<Rep, Period>& duration) {
47 pthread_spin_lock(&lock_);
50 pthread_spin_unlock(&lock_);
55 waiters_.push_back(waiter);
56 pthread_spin_unlock(&lock_);
58 if (!waiter.baton.timed_wait(duration)) {
59 // We timed out. Two cases:
60 // 1. We're still in the waiter list and we truly timed out
61 // 2. We're not in the waiter list anymore. This could happen if the baton
62 // times out but the mutex is unlocked before we reach this code. In this
63 // case we'll pretend we got the lock on time.
64 pthread_spin_lock(&lock_);
65 if (waiter.hook.is_linked()) {
66 waiters_.erase(waiters_.iterator_to(waiter));
67 pthread_spin_unlock(&lock_);
70 pthread_spin_unlock(&lock_);
75 template <typename BatonType>
76 bool TimedMutex<BatonType>::try_lock() {
77 pthread_spin_lock(&lock_);
79 pthread_spin_unlock(&lock_);
83 pthread_spin_unlock(&lock_);
87 template <typename BatonType>
88 void TimedMutex<BatonType>::unlock() {
89 pthread_spin_lock(&lock_);
90 if (waiters_.empty()) {
92 pthread_spin_unlock(&lock_);
95 MutexWaiter& to_wake = waiters_.front();
98 pthread_spin_unlock(&lock_);
102 // TimedRWMutex implementation
105 template <typename BatonType>
106 void TimedRWMutex<BatonType>::read_lock() {
107 pthread_spin_lock(&lock_);
108 if (state_ == State::WRITE_LOCKED) {
110 read_waiters_.push_back(waiter);
111 pthread_spin_unlock(&lock_);
113 assert(state_ == State::READ_LOCKED);
116 assert((state_ == State::UNLOCKED && readers_ == 0) ||
117 (state_ == State::READ_LOCKED && readers_ > 0));
118 assert(read_waiters_.empty());
119 state_ = State::READ_LOCKED;
121 pthread_spin_unlock(&lock_);
124 template <typename BatonType>
125 template <typename Rep, typename Period>
126 bool TimedRWMutex<BatonType>::timed_read_lock(
127 const std::chrono::duration<Rep, Period>& duration) {
128 pthread_spin_lock(&lock_);
129 if (state_ == State::WRITE_LOCKED) {
131 read_waiters_.push_back(waiter);
132 pthread_spin_unlock(&lock_);
134 if (!waiter.baton.timed_wait(duration)) {
135 // We timed out. Two cases:
136 // 1. We're still in the waiter list and we truly timed out
137 // 2. We're not in the waiter list anymore. This could happen if the baton
138 // times out but the mutex is unlocked before we reach this code. In
139 // this case we'll pretend we got the lock on time.
140 pthread_spin_lock(&lock_);
141 if (waiter.hook.is_linked()) {
142 read_waiters_.erase(read_waiters_.iterator_to(waiter));
143 pthread_spin_unlock(&lock_);
146 pthread_spin_unlock(&lock_);
150 assert((state_ == State::UNLOCKED && readers_ == 0) ||
151 (state_ == State::READ_LOCKED && readers_ > 0));
152 assert(read_waiters_.empty());
153 state_ = State::READ_LOCKED;
155 pthread_spin_unlock(&lock_);
159 template <typename BatonType>
160 bool TimedRWMutex<BatonType>::try_read_lock() {
161 pthread_spin_lock(&lock_);
162 if (state_ != State::WRITE_LOCKED) {
163 assert((state_ == State::UNLOCKED && readers_ == 0) ||
164 (state_ == State::READ_LOCKED && readers_ > 0));
165 assert(read_waiters_.empty());
166 state_ = State::READ_LOCKED;
168 pthread_spin_unlock(&lock_);
171 pthread_spin_unlock(&lock_);
175 template <typename BatonType>
176 void TimedRWMutex<BatonType>::write_lock() {
177 pthread_spin_lock(&lock_);
178 if (state_ == State::UNLOCKED) {
179 verify_unlocked_properties();
180 state_ = State::WRITE_LOCKED;
181 pthread_spin_unlock(&lock_);
185 write_waiters_.push_back(waiter);
186 pthread_spin_unlock(&lock_);
190 template <typename BatonType>
191 template <typename Rep, typename Period>
192 bool TimedRWMutex<BatonType>::timed_write_lock(
193 const std::chrono::duration<Rep, Period>& duration) {
194 pthread_spin_lock(&lock_);
195 if (state_ == State::UNLOCKED) {
196 verify_unlocked_properties();
197 state_ = State::WRITE_LOCKED;
198 pthread_spin_unlock(&lock_);
202 write_waiters_.push_back(waiter);
203 pthread_spin_unlock(&lock_);
205 if (!waiter.baton.timed_wait(duration)) {
206 // We timed out. Two cases:
207 // 1. We're still in the waiter list and we truly timed out
208 // 2. We're not in the waiter list anymore. This could happen if the baton
209 // times out but the mutex is unlocked before we reach this code. In
210 // this case we'll pretend we got the lock on time.
211 pthread_spin_lock(&lock_);
212 if (waiter.hook.is_linked()) {
213 write_waiters_.erase(write_waiters_.iterator_to(waiter));
214 pthread_spin_unlock(&lock_);
217 pthread_spin_unlock(&lock_);
219 assert(state_ == State::WRITE_LOCKED);
223 template <typename BatonType>
224 bool TimedRWMutex<BatonType>::try_write_lock() {
225 pthread_spin_lock(&lock_);
226 if (state_ == State::UNLOCKED) {
227 verify_unlocked_properties();
228 state_ = State::WRITE_LOCKED;
229 pthread_spin_unlock(&lock_);
232 pthread_spin_unlock(&lock_);
236 template <typename BatonType>
237 void TimedRWMutex<BatonType>::unlock() {
238 pthread_spin_lock(&lock_);
239 assert(state_ != State::UNLOCKED);
240 assert((state_ == State::READ_LOCKED && readers_ > 0) ||
241 (state_ == State::WRITE_LOCKED && readers_ == 0));
242 if (state_ == State::READ_LOCKED) {
246 if (!read_waiters_.empty()) {
247 assert(state_ == State::WRITE_LOCKED && readers_ == 0 &&
248 "read waiters can only accumulate while write locked");
249 state_ = State::READ_LOCKED;
250 readers_ = read_waiters_.size();
252 while (!read_waiters_.empty()) {
253 MutexWaiter& to_wake = read_waiters_.front();
254 read_waiters_.pop_front();
255 to_wake.baton.post();
257 } else if (readers_ == 0) {
258 if (!write_waiters_.empty()) {
259 assert(read_waiters_.empty());
260 state_ = State::WRITE_LOCKED;
262 // Wake a single writer (after releasing the spin lock)
263 MutexWaiter& to_wake = write_waiters_.front();
264 write_waiters_.pop_front();
265 to_wake.baton.post();
267 verify_unlocked_properties();
268 state_ = State::UNLOCKED;
271 assert(state_ == State::READ_LOCKED);
273 pthread_spin_unlock(&lock_);
276 template <typename BatonType>
277 void TimedRWMutex<BatonType>::downgrade() {
278 pthread_spin_lock(&lock_);
279 assert(state_ == State::WRITE_LOCKED && readers_ == 0);
280 state_ = State::READ_LOCKED;
283 if (!read_waiters_.empty()) {
284 readers_ += read_waiters_.size();
286 while (!read_waiters_.empty()) {
287 MutexWaiter& to_wake = read_waiters_.front();
288 read_waiters_.pop_front();
289 to_wake.baton.post();
292 pthread_spin_unlock(&lock_);