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.
18 #include <folly/detail/MemoryIdler.h>
20 namespace folly { namespace fibers {
26 bool Baton::timed_wait(TimeoutController::Duration timeout) {
27 return timed_wait(timeout, [](){});
30 void Baton::waitThread() {
31 if (spinWaitForEarlyPost()) {
32 assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
36 auto fiber = waitingFiber_.load();
38 if (LIKELY(fiber == NO_WAITER &&
39 waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
41 folly::detail::MemoryIdler::futexWait(futex_.futex, THREAD_WAITING);
42 fiber = waitingFiber_.load(std::memory_order_relaxed);
43 } while (fiber == THREAD_WAITING);
46 if (LIKELY(fiber == POSTED)) {
51 if (fiber == TIMEOUT) {
52 throw std::logic_error("Thread baton can't have timeout status");
54 if (fiber == THREAD_WAITING) {
55 throw std::logic_error("Other thread is already waiting on this baton");
57 throw std::logic_error("Other fiber is already waiting on this baton");
60 bool Baton::spinWaitForEarlyPost() {
61 static_assert(PreBlockAttempts > 0,
62 "isn't this assert clearer than an uninitialized variable warning?");
63 for (int i = 0; i < PreBlockAttempts; ++i) {
68 // The pause instruction is the polite way to spin, but it doesn't
69 // actually affect correctness to omit it if we don't have it.
70 // Pausing donates the full capabilities of the current core to
71 // its other hyperthreads for a dozen cycles or so
78 bool Baton::timedWaitThread(TimeoutController::Duration timeout) {
79 if (spinWaitForEarlyPost()) {
80 assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
84 auto fiber = waitingFiber_.load();
86 if (LIKELY(fiber == NO_WAITER &&
87 waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
88 auto deadline = TimeoutController::Clock::now() + timeout;
91 futex_.futex.futexWaitUntil(THREAD_WAITING, deadline);
92 if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {
95 fiber = waitingFiber_.load(std::memory_order_relaxed);
96 } while (fiber == THREAD_WAITING);
99 if (LIKELY(fiber == POSTED)) {
104 if (fiber == TIMEOUT) {
105 throw std::logic_error("Thread baton can't have timeout status");
107 if (fiber == THREAD_WAITING) {
108 throw std::logic_error("Other thread is already waiting on this baton");
110 throw std::logic_error("Other fiber is already waiting on this baton");
117 void Baton::postHelper(intptr_t new_value) {
118 auto fiber = waitingFiber_.load();
121 if (fiber == THREAD_WAITING) {
122 assert(new_value == POSTED);
127 if (fiber == POSTED || fiber == TIMEOUT) {
130 } while (!waitingFiber_.compare_exchange_weak(fiber, new_value));
132 if (fiber != NO_WAITER) {
133 reinterpret_cast<Fiber*>(fiber)->setData(0);
137 bool Baton::try_wait() {
138 auto state = waitingFiber_.load();
139 return state == POSTED;
142 void Baton::postThread() {
143 auto expected = THREAD_WAITING;
145 if (!waitingFiber_.compare_exchange_strong(expected, POSTED)) {
149 futex_.futex.futexWake(1);
152 void Baton::reset() {
153 waitingFiber_.store(NO_WAITER, std::memory_order_relaxed);;