proxygen
Mutex.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018-present Facebook, Inc.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #include <folly/Portability.h>
18 
19 #if FOLLY_HAS_COROUTINES
20 
22 
23 #include <cassert>
24 
25 using namespace folly::coro;
26 
27 Mutex::~Mutex() {
28  // Check there are no waiters waiting to acquire the lock.
29  assert(
30  state_.load(std::memory_order_relaxed) == unlockedState() ||
31  state_.load(std::memory_order_relaxed) == nullptr);
32  assert(waiters_ == nullptr);
33 }
34 
35 void Mutex::unlock() noexcept {
36  assert(state_.load(std::memory_order_relaxed) != unlockedState());
37 
38  auto* waitersHead = waiters_;
39  if (waitersHead == nullptr) {
40  void* currentState = state_.load(std::memory_order_relaxed);
41  if (currentState == nullptr) {
42  // Looks like there are no waiters waiting to acquire the lock.
43  // Try to unlock it - use a compare-exchange to decide the race between
44  // unlocking the mutex and another thread enqueueing another waiter.
45  const bool releasedLock = state_.compare_exchange_strong(
46  currentState,
47  unlockedState(),
48  std::memory_order_release,
49  std::memory_order_relaxed);
50  if (releasedLock) {
51  return;
52  }
53  }
54 
55  // There are some awaiters that have been newly queued.
56  // Dequeue them and reverse their order from LIFO to FIFO.
57  currentState = state_.exchange(nullptr, std::memory_order_acquire);
58 
59  assert(currentState != unlockedState());
60  assert(currentState != nullptr);
61 
62  auto* waiter = static_cast<LockOperation*>(currentState);
63  do {
64  auto* temp = waiter->next_;
65  waiter->next_ = waitersHead;
66  waitersHead = waiter;
67  waiter = temp;
68  } while (waiter != nullptr);
69  }
70 
71  assert(waitersHead != nullptr);
72 
73  waiters_ = waitersHead->next_;
74 
75  waitersHead->awaitingCoroutine_.resume();
76 }
77 
78 bool Mutex::lockAsyncImpl(LockOperation* awaiter) {
79  void* oldValue = state_.load(std::memory_order_relaxed);
80  while (true) {
81  if (oldValue == unlockedState()) {
82  // It looks like the mutex is currently unlocked.
83  // Try to acquire it synchronously.
84  void* newValue = nullptr;
85  if (state_.compare_exchange_weak(
86  oldValue,
87  newValue,
88  std::memory_order_acquire,
89  std::memory_order_relaxed)) {
90  // Acquired synchronously, don't suspend.
91  return false;
92  }
93  } else {
94  // It looks like the mutex is currently locked.
95  // Try to queue this waiter to the list of waiters.
96  void* newValue = awaiter;
97  awaiter->next_ = static_cast<LockOperation*>(oldValue);
98  if (state_.compare_exchange_weak(
99  oldValue,
100  newValue,
101  std::memory_order_release,
102  std::memory_order_relaxed)) {
103  // Queued waiter successfully. Awaiting coroutine should suspend.
104  return true;
105  }
106  }
107  }
108 }
109 
110 #endif
std::atomic< void * > state_
Definition: Mutex.h:210
LockOperation * waiters_
Definition: Mutex.h:214
requires E e noexcept(noexcept(s.error(std::move(e))))
void * unlockedState() noexcept
Definition: Mutex.h:192
friend class LockOperation
Definition: Mutex.h:189
void unlock() noexcept
bool lockAsyncImpl(LockOperation *awaiter)
std::experimental::coroutine_handle awaitingCoroutine_
Definition: Mutex.h:174