2 * Copyright 2016 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.
20 #include <folly/detail/MemoryIdler.h>
21 #include <folly/experimental/fibers/FiberManager.h>
23 namespace folly { namespace fibers {
29 void Baton::wait(TimeoutHandler& timeoutHandler) {
30 auto timeoutFunc = [this, &timeoutHandler] {
34 timeoutHandler.timeoutPtr_ = 0;
36 timeoutHandler.timeoutFunc_ = std::ref(timeoutFunc);
37 timeoutHandler.fiberManager_ = FiberManager::getFiberManagerUnsafe();
39 timeoutHandler.cancelTimeout();
42 bool Baton::timed_wait(TimeoutController::Duration timeout) {
43 return timed_wait(timeout, [](){});
46 void Baton::waitThread() {
47 if (spinWaitForEarlyPost()) {
48 assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
52 auto fiber = waitingFiber_.load();
54 if (LIKELY(fiber == NO_WAITER &&
55 waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
57 folly::detail::MemoryIdler::futexWait(futex_.futex, THREAD_WAITING);
58 fiber = waitingFiber_.load(std::memory_order_relaxed);
59 } while (fiber == THREAD_WAITING);
62 if (LIKELY(fiber == POSTED)) {
67 if (fiber == TIMEOUT) {
68 throw std::logic_error("Thread baton can't have timeout status");
70 if (fiber == THREAD_WAITING) {
71 throw std::logic_error("Other thread is already waiting on this baton");
73 throw std::logic_error("Other fiber is already waiting on this baton");
76 bool Baton::spinWaitForEarlyPost() {
77 static_assert(PreBlockAttempts > 0,
78 "isn't this assert clearer than an uninitialized variable warning?");
79 for (int i = 0; i < PreBlockAttempts; ++i) {
84 // The pause instruction is the polite way to spin, but it doesn't
85 // actually affect correctness to omit it if we don't have it.
86 // Pausing donates the full capabilities of the current core to
87 // its other hyperthreads for a dozen cycles or so
94 bool Baton::timedWaitThread(TimeoutController::Duration timeout) {
95 if (spinWaitForEarlyPost()) {
96 assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
100 auto fiber = waitingFiber_.load();
102 if (LIKELY(fiber == NO_WAITER &&
103 waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
104 auto deadline = TimeoutController::Clock::now() + timeout;
107 futex_.futex.futexWaitUntil(THREAD_WAITING, deadline);
108 if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {
111 fiber = waitingFiber_.load(std::memory_order_relaxed);
112 } while (fiber == THREAD_WAITING);
115 if (LIKELY(fiber == POSTED)) {
120 if (fiber == TIMEOUT) {
121 throw std::logic_error("Thread baton can't have timeout status");
123 if (fiber == THREAD_WAITING) {
124 throw std::logic_error("Other thread is already waiting on this baton");
126 throw std::logic_error("Other fiber is already waiting on this baton");
133 void Baton::postHelper(intptr_t new_value) {
134 auto fiber = waitingFiber_.load();
137 if (fiber == THREAD_WAITING) {
138 assert(new_value == POSTED);
143 if (fiber == POSTED || fiber == TIMEOUT) {
146 } while (!waitingFiber_.compare_exchange_weak(fiber, new_value));
148 if (fiber != NO_WAITER) {
149 reinterpret_cast<Fiber*>(fiber)->setData(0);
153 bool Baton::try_wait() {
154 auto state = waitingFiber_.load();
155 return state == POSTED;
158 void Baton::postThread() {
159 auto expected = THREAD_WAITING;
161 if (!waitingFiber_.compare_exchange_strong(expected, POSTED)) {
165 futex_.futex.futexWake(1);
168 void Baton::reset() {
169 waitingFiber_.store(NO_WAITER, std::memory_order_relaxed);;
172 void Baton::TimeoutHandler::scheduleTimeout(
173 TimeoutController::Duration timeout) {
174 assert(fiberManager_ != nullptr);
175 assert(timeoutFunc_ != nullptr);
176 assert(timeoutPtr_ == 0);
178 if (timeout.count() > 0) {
179 timeoutPtr_ = fiberManager_->timeoutManager_->registerTimeout(
180 timeoutFunc_, timeout);
184 void Baton::TimeoutHandler::cancelTimeout() {
186 fiberManager_->timeoutManager_->cancel(timeoutPtr_);