2 * Copyright 2014-present 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/Futex.h>
21 #include <folly/fibers/TimeoutController.h>
32 * Primitive which allows one to put current Fiber to sleep and wake it from
33 * another Fiber/thread.
44 auto state = waitingFiber_.load();
45 return state == POSTED;
49 * Puts active fiber to sleep. Returns when post is called.
54 * Put active fiber to sleep indefinitely. However, timeoutHandler may
55 * be used elsewhere on the same thread in order to schedule a wakeup
56 * for the active fiber. Users of timeoutHandler must be on the same thread
57 * as the active fiber and may only schedule one timeout, which must occur
58 * after the active fiber calls wait.
60 void wait(TimeoutHandler& timeoutHandler);
63 * Puts active fiber to sleep. Returns when post is called.
65 * @param mainContextFunc this function is immediately executed on the main
69 void wait(F&& mainContextFunc);
72 * Checks if the baton has been posted without blocking.
74 * @return true iff the baton has been posted.
79 * Puts active fiber to sleep. Returns when post is called or the timeout
82 * @param timeout Baton will be automatically awaken if timeout expires
84 * @return true if was posted, false if timeout expired
86 template <typename Rep, typename Period>
87 bool try_wait_for(const std::chrono::duration<Rep, Period>& timeout) {
88 return try_wait_for(timeout, [] {});
92 * Puts active fiber to sleep. Returns when post is called or the timeout
95 * @param timeout Baton will be automatically awaken if timeout expires
96 * @param mainContextFunc this function is immediately executed on the main
99 * @return true if was posted, false if timeout expired
101 template <typename Rep, typename Period, typename F>
103 const std::chrono::duration<Rep, Period>& timeout,
104 F&& mainContextFunc);
107 * Puts active fiber to sleep. Returns when post is called or the deadline
110 * @param timeout Baton will be automatically awaken if deadline expires
112 * @return true if was posted, false if timeout expired
114 template <typename Clock, typename Duration>
116 const std::chrono::time_point<Clock, Duration>& deadline) {
117 return try_wait_until(deadline, [] {});
121 * Puts active fiber to sleep. Returns when post is called or the deadline
124 * @param timeout Baton will be automatically awaken if deadline expires
125 * @param mainContextFunc this function is immediately executed on the main
128 * @return true if was posted, false if timeout expired
130 template <typename Clock, typename Duration, typename F>
132 const std::chrono::time_point<Clock, Duration>& deadline,
133 F&& mainContextFunc);
136 * Puts active fiber to sleep. Returns when post is called or the deadline
139 * @param timeout Baton will be automatically awaken if deadline expires
140 * @param mainContextFunc this function is immediately executed on the main
143 * @return true if was posted, false if timeout expired
145 template <typename Clock, typename Duration, typename F>
147 const std::chrono::time_point<Clock, Duration>& deadline,
148 F&& mainContextFunc);
150 /// Alias to try_wait_for. Deprecated.
151 template <typename Rep, typename Period>
152 bool timed_wait(const std::chrono::duration<Rep, Period>& timeout) {
153 return try_wait_for(timeout);
156 /// Alias to try_wait_for. Deprecated.
157 template <typename Rep, typename Period, typename F>
159 const std::chrono::duration<Rep, Period>& timeout,
160 F&& mainContextFunc) {
161 return try_wait_for(timeout, static_cast<F&&>(mainContextFunc));
164 /// Alias to try_wait_until. Deprecated.
165 template <typename Clock, typename Duration>
166 bool timed_wait(const std::chrono::time_point<Clock, Duration>& deadline) {
167 return try_wait_until(deadline);
170 /// Alias to try_wait_until. Deprecated.
171 template <typename Clock, typename Duration, typename F>
173 const std::chrono::time_point<Clock, Duration>& deadline,
174 F&& mainContextFunc) {
175 return try_wait_until(deadline, static_cast<F&&>(mainContextFunc));
179 * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
180 * next wait() call will return immediately).
185 * Reset's the baton (equivalent to destroying the object and constructing
186 * another one in place).
187 * Caller is responsible for making sure no one is waiting on/posting the
188 * baton when reset() is called.
193 * Provides a way to schedule a wakeup for a wait()ing fiber.
194 * A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)
195 * before a timeout is scheduled. It is only safe to use the
196 * TimeoutHandler on the same thread as the wait()ing fiber.
197 * scheduleTimeout() may only be called once prior to the end of the
198 * associated Baton's life.
200 class TimeoutHandler {
202 void scheduleTimeout(TimeoutController::Duration timeoutMs);
207 void cancelTimeout();
209 std::function<void()> timeoutFunc_{nullptr};
210 FiberManager* fiberManager_{nullptr};
212 intptr_t timeoutPtr_{0};
218 * Must be positive. If multiple threads are actively using a
219 * higher-level data structure that uses batons internally, it is
220 * likely that the post() and wait() calls happen almost at the same
221 * time. In this state, we lose big 50% of the time if the wait goes
222 * to sleep immediately. On circa-2013 devbox hardware it costs about
223 * 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
224 * posix_sem_pingpong test in BatonTests). We can improve our chances
225 * of early post by spinning for a bit, although we have to balance
226 * this against the loss if we end up sleeping any way. Spins on this
227 * hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
228 * We give ourself 300 spins, which is about 2 usec of waiting. As a
229 * partial consolation, since we are using the pause instruction we
230 * are giving a speed boost to the colocated hyperthread.
232 PreBlockAttempts = 300,
235 explicit Baton(intptr_t state) : waitingFiber_(state) {}
237 void postHelper(intptr_t new_value);
241 template <typename F>
242 inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
244 * Spin for "some time" (see discussion on PreBlockAttempts) waiting
246 * @return true if we received a post the spin wait, false otherwise. If the
247 * function returns true then Baton state is guaranteed to be POSTED
249 bool spinWaitForEarlyPost();
251 bool timedWaitThread(TimeoutController::Duration timeout);
253 static constexpr intptr_t NO_WAITER = 0;
254 static constexpr intptr_t POSTED = -1;
255 static constexpr intptr_t TIMEOUT = -2;
256 static constexpr intptr_t THREAD_WAITING = -3;
259 std::atomic<intptr_t> waitingFiber_;
261 folly::detail::Futex<> futex{};
262 int32_t _unused_packing;
266 } // namespace fibers
269 #include <folly/fibers/Baton-inl.h>