2 * Copyright 2017-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 * Puts active fiber to sleep. Returns when post is called.
49 * Put active fiber to sleep indefinitely. However, timeoutHandler may
50 * be used elsewhere on the same thread in order to schedule a wakeup
51 * for the active fiber. Users of timeoutHandler must be on the same thread
52 * as the active fiber and may only schedule one timeout, which must occur
53 * after the active fiber calls wait.
55 void wait(TimeoutHandler& timeoutHandler);
58 * Puts active fiber to sleep. Returns when post is called.
60 * @param mainContextFunc this function is immediately executed on the main
64 void wait(F&& mainContextFunc);
67 * Checks if the baton has been posted without blocking.
69 * @return true iff the baton has been posted.
74 * Puts active fiber to sleep. Returns when post is called or the timeout
77 * @param timeout Baton will be automatically awaken if timeout expires
79 * @return true if was posted, false if timeout expired
81 template <typename Rep, typename Period>
82 bool try_wait_for(const std::chrono::duration<Rep, Period>& timeout) {
83 return try_wait_for(timeout, [] {});
87 * Puts active fiber to sleep. Returns when post is called or the timeout
90 * @param timeout Baton will be automatically awaken if timeout expires
91 * @param mainContextFunc this function is immediately executed on the main
94 * @return true if was posted, false if timeout expired
96 template <typename Rep, typename Period, typename F>
98 const std::chrono::duration<Rep, Period>& timeout,
102 * Puts active fiber to sleep. Returns when post is called or the deadline
105 * @param timeout Baton will be automatically awaken if deadline expires
107 * @return true if was posted, false if timeout expired
109 template <typename Clock, typename Duration>
111 const std::chrono::time_point<Clock, Duration>& deadline) {
112 return try_wait_until(deadline, [] {});
116 * Puts active fiber to sleep. Returns when post is called or the deadline
119 * @param timeout Baton will be automatically awaken if deadline expires
120 * @param mainContextFunc this function is immediately executed on the main
123 * @return true if was posted, false if timeout expired
125 template <typename Clock, typename Duration, typename F>
127 const std::chrono::time_point<Clock, Duration>& deadline,
128 F&& mainContextFunc);
131 * Puts active fiber to sleep. Returns when post is called or the deadline
134 * @param timeout Baton will be automatically awaken if deadline expires
135 * @param mainContextFunc this function is immediately executed on the main
138 * @return true if was posted, false if timeout expired
140 template <typename Clock, typename Duration, typename F>
142 const std::chrono::time_point<Clock, Duration>& deadline,
143 F&& mainContextFunc);
145 /// Alias to try_wait_for. Deprecated.
146 template <typename Rep, typename Period>
147 bool timed_wait(const std::chrono::duration<Rep, Period>& timeout) {
148 return try_wait_for(timeout);
151 /// Alias to try_wait_for. Deprecated.
152 template <typename Rep, typename Period, typename F>
154 const std::chrono::duration<Rep, Period>& timeout,
155 F&& mainContextFunc) {
156 return try_wait_for(timeout, static_cast<F&&>(mainContextFunc));
159 /// Alias to try_wait_until. Deprecated.
160 template <typename Clock, typename Duration>
161 bool timed_wait(const std::chrono::time_point<Clock, Duration>& deadline) {
162 return try_wait_until(deadline);
165 /// Alias to try_wait_until. Deprecated.
166 template <typename Clock, typename Duration, typename F>
168 const std::chrono::time_point<Clock, Duration>& deadline,
169 F&& mainContextFunc) {
170 return try_wait_until(deadline, static_cast<F&&>(mainContextFunc));
174 * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
175 * next wait() call will return immediately).
180 * Reset's the baton (equivalent to destroying the object and constructing
181 * another one in place).
182 * Caller is responsible for making sure no one is waiting on/posting the
183 * baton when reset() is called.
188 * Provides a way to schedule a wakeup for a wait()ing fiber.
189 * A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)
190 * before a timeout is scheduled. It is only safe to use the
191 * TimeoutHandler on the same thread as the wait()ing fiber.
192 * scheduleTimeout() may only be called once prior to the end of the
193 * associated Baton's life.
195 class TimeoutHandler {
197 void scheduleTimeout(TimeoutController::Duration timeoutMs);
202 void cancelTimeout();
204 std::function<void()> timeoutFunc_{nullptr};
205 FiberManager* fiberManager_{nullptr};
207 intptr_t timeoutPtr_{0};
213 * Must be positive. If multiple threads are actively using a
214 * higher-level data structure that uses batons internally, it is
215 * likely that the post() and wait() calls happen almost at the same
216 * time. In this state, we lose big 50% of the time if the wait goes
217 * to sleep immediately. On circa-2013 devbox hardware it costs about
218 * 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
219 * posix_sem_pingpong test in BatonTests). We can improve our chances
220 * of early post by spinning for a bit, although we have to balance
221 * this against the loss if we end up sleeping any way. Spins on this
222 * hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
223 * We give ourself 300 spins, which is about 2 usec of waiting. As a
224 * partial consolation, since we are using the pause instruction we
225 * are giving a speed boost to the colocated hyperthread.
227 PreBlockAttempts = 300,
230 explicit Baton(intptr_t state) : waitingFiber_(state) {}
232 void postHelper(intptr_t new_value);
236 template <typename F>
237 inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
239 * Spin for "some time" (see discussion on PreBlockAttempts) waiting
241 * @return true if we received a post the spin wait, false otherwise. If the
242 * function returns true then Baton state is guaranteed to be POSTED
244 bool spinWaitForEarlyPost();
246 bool timedWaitThread(TimeoutController::Duration timeout);
248 static constexpr intptr_t NO_WAITER = 0;
249 static constexpr intptr_t POSTED = -1;
250 static constexpr intptr_t TIMEOUT = -2;
251 static constexpr intptr_t THREAD_WAITING = -3;
254 std::atomic<intptr_t> waitingFiber_;
256 folly::detail::Futex<> futex;
257 int32_t _unused_packing;
261 } // namespace fibers
264 #include <folly/fibers/Baton-inl.h>