proxygen
Baton.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2014-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 #include <folly/fibers/Baton.h>
17 
18 #include <chrono>
19 
22 #include <folly/portability/Asm.h>
23 
24 namespace folly {
25 namespace fibers {
26 
29 
30 void Baton::setWaiter(Waiter& waiter) {
31  auto curr_waiter = waiter_.load();
32  do {
33  if (LIKELY(curr_waiter == NO_WAITER)) {
34  continue;
35  } else if (curr_waiter == POSTED || curr_waiter == TIMEOUT) {
36  waiter.post();
37  break;
38  } else {
39  throw std::logic_error("Some waiter is already waiting on this Baton.");
40  }
41  } while (!waiter_.compare_exchange_weak(
42  curr_waiter, reinterpret_cast<intptr_t>(&waiter)));
43 }
44 
45 void Baton::wait() {
46  wait([]() {});
47 }
48 
49 void Baton::wait(TimeoutHandler& timeoutHandler) {
50  auto timeoutFunc = [this, &timeoutHandler] {
51  if (!try_wait()) {
53  }
54  timeoutHandler.timeoutPtr_ = 0;
55  };
56  timeoutHandler.timeoutFunc_ = std::ref(timeoutFunc);
58  wait();
59  timeoutHandler.cancelTimeout();
60 }
61 
63  if (spinWaitForEarlyPost()) {
64  assert(waiter_.load(std::memory_order_acquire) == POSTED);
65  return;
66  }
67 
68  auto waiter = waiter_.load();
69 
70  if (LIKELY(
71  waiter == NO_WAITER &&
72  waiter_.compare_exchange_strong(waiter, THREAD_WAITING))) {
73  do {
76  waiter = waiter_.load(std::memory_order_acquire);
77  } while (waiter == THREAD_WAITING);
78  }
79 
80  if (LIKELY(waiter == POSTED)) {
81  return;
82  }
83 
84  // Handle errors
85  if (waiter == TIMEOUT) {
86  throw std::logic_error("Thread baton can't have timeout status");
87  }
88  if (waiter == THREAD_WAITING) {
89  throw std::logic_error("Other thread is already waiting on this baton");
90  }
91  throw std::logic_error("Other waiter is already waiting on this baton");
92 }
93 
95  static_assert(
96  PreBlockAttempts > 0,
97  "isn't this assert clearer than an uninitialized variable warning?");
98  for (int i = 0; i < PreBlockAttempts; ++i) {
99  if (try_wait()) {
100  // hooray!
101  return true;
102  }
103  // The pause instruction is the polite way to spin, but it doesn't
104  // actually affect correctness to omit it if we don't have it.
105  // Pausing donates the full capabilities of the current core to
106  // its other hyperthreads for a dozen cycles or so
108  }
109 
110  return false;
111 }
112 
114  if (spinWaitForEarlyPost()) {
115  assert(waiter_.load(std::memory_order_acquire) == POSTED);
116  return true;
117  }
118 
119  auto waiter = waiter_.load();
120 
121  if (LIKELY(
122  waiter == NO_WAITER &&
123  waiter_.compare_exchange_strong(waiter, THREAD_WAITING))) {
124  auto deadline = TimeoutController::Clock::now() + timeout;
125  do {
126  auto* futex = &futex_.futex;
127  const auto wait_rv =
129  if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {
130  return false;
131  }
132  waiter = waiter_.load(std::memory_order_relaxed);
133  } while (waiter == THREAD_WAITING);
134  }
135 
136  if (LIKELY(waiter == POSTED)) {
137  return true;
138  }
139 
140  // Handle errors
141  if (waiter == TIMEOUT) {
142  throw std::logic_error("Thread baton can't have timeout status");
143  }
144  if (waiter == THREAD_WAITING) {
145  throw std::logic_error("Other thread is already waiting on this baton");
146  }
147  throw std::logic_error("Other waiter is already waiting on this baton");
148 }
149 
150 void Baton::post() {
152 }
153 
154 void Baton::postHelper(intptr_t new_value) {
155  auto waiter = waiter_.load();
156 
157  do {
158  if (waiter == THREAD_WAITING) {
159  assert(new_value == POSTED);
160 
161  return postThread();
162  }
163 
164  if (waiter == POSTED || waiter == TIMEOUT) {
165  return;
166  }
167  } while (!waiter_.compare_exchange_weak(waiter, new_value));
168 
169  if (waiter != NO_WAITER) {
170  reinterpret_cast<Waiter*>(waiter)->post();
171  }
172 }
173 
175  return ready();
176 }
177 
179  auto expected = THREAD_WAITING;
180 
181  auto* futex = &futex_.futex;
182  if (!waiter_.compare_exchange_strong(expected, POSTED)) {
183  return;
184  }
185  futexWake(futex, 1);
186 }
187 
188 void Baton::reset() {
189  waiter_.store(NO_WAITER, std::memory_order_relaxed);
190 }
191 
193  TimeoutController::Duration timeout) {
194  assert(fiberManager_ != nullptr);
195  assert(timeoutFunc_ != nullptr);
196  assert(timeoutPtr_ == 0);
197 
198  if (timeout.count() > 0) {
199  timeoutPtr_ =
200  fiberManager_->timeoutManager_->registerTimeout(timeoutFunc_, timeout);
201  }
202 }
203 
205  if (timeoutPtr_) {
206  fiberManager_->timeoutManager_->cancel(timeoutPtr_);
207  }
208 }
209 } // namespace fibers
210 } // namespace folly
static constexpr intptr_t NO_WAITER
Definition: Baton.h:273
static constexpr intptr_t POSTED
Definition: Baton.h:274
FutexResult futexWaitUntil(const Futex< MockAtom > *futex, std::uint32_t expected, std::chrono::time_point< Clock, Duration > const &deadline, uint32_t waitMask)
static FutexResult futexWait(Futex &fut, uint32_t expected, uint32_t waitMask=-1, IdleTime const &idleTimeout=defaultIdleTimeout.load(std::memory_order_acquire), size_t stackToRetain=kDefaultStackToRetain, float timeoutVariationFrac=0.5)
Definition: MemoryIdler.h:102
static constexpr intptr_t TIMEOUT
Definition: Baton.h:275
struct folly::fibers::Baton::@51::@53 futex_
std::chrono::steady_clock::time_point now()
#define LIKELY(x)
Definition: Likely.h:47
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
std::atomic< intptr_t > waiter_
Definition: Baton.h:279
static constexpr intptr_t THREAD_WAITING
Definition: Baton.h:276
void scheduleTimeout(TimeoutController::Duration timeoutMs)
Definition: Baton.cpp:192
std::function< void()> timeoutFunc_
Definition: Baton.h:227
bool timedWaitThread(TimeoutController::Duration timeout)
Definition: Baton.cpp:113
FutexResult futexWaitUntil(const Futex *futex, uint32_t expected, std::chrono::time_point< Clock, Duration > const &deadline, uint32_t waitMask)
Definition: Futex-inl.h:112
void setWaiter(Waiter &waiter)
Definition: Baton.cpp:30
folly::detail::Futex futex
Definition: Baton.h:281
static FiberManager * getFiberManagerUnsafe()
bool ready() const
Definition: Baton.h:55
void postHelper(intptr_t new_value)
Definition: Baton.cpp:154
bool spinWaitForEarlyPost()
Definition: Baton.cpp:94
int futexWake(const Futex *futex, int count, uint32_t wakeMask)
Definition: Futex-inl.h:107
void asm_volatile_pause()
Definition: Asm.h:37