folly::fibers::Baton API consistency with folly::Baton
[folly.git] / folly / fibers / Baton.h
1 /*
2  * Copyright 2017-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 #pragma once
17
18 #include <atomic>
19
20 #include <folly/detail/Futex.h>
21 #include <folly/fibers/TimeoutController.h>
22
23 namespace folly {
24 namespace fibers {
25
26 class Fiber;
27 class FiberManager;
28
29 /**
30  * @class Baton
31  *
32  * Primitive which allows one to put current Fiber to sleep and wake it from
33  * another Fiber/thread.
34  */
35 class Baton {
36  public:
37   class TimeoutHandler;
38
39   Baton();
40
41   ~Baton() {}
42
43   /**
44    * Puts active fiber to sleep. Returns when post is called.
45    */
46   void wait();
47
48   /**
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.
54    */
55   void wait(TimeoutHandler& timeoutHandler);
56
57   /**
58    * Puts active fiber to sleep. Returns when post is called.
59    *
60    * @param mainContextFunc this function is immediately executed on the main
61    *        context.
62    */
63   template <typename F>
64   void wait(F&& mainContextFunc);
65
66   /**
67    * Checks if the baton has been posted without blocking.
68    *
69    * @return    true iff the baton has been posted.
70    */
71   bool try_wait();
72
73   /**
74    * Puts active fiber to sleep. Returns when post is called or the timeout
75    * expires.
76    *
77    * @param timeout Baton will be automatically awaken if timeout expires
78    *
79    * @return true if was posted, false if timeout expired
80    */
81   template <typename Rep, typename Period>
82   bool try_wait_for(const std::chrono::duration<Rep, Period>& timeout) {
83     return try_wait_for(timeout, [] {});
84   }
85
86   /**
87    * Puts active fiber to sleep. Returns when post is called or the timeout
88    * expires.
89    *
90    * @param timeout Baton will be automatically awaken if timeout expires
91    * @param mainContextFunc this function is immediately executed on the main
92    *        context.
93    *
94    * @return true if was posted, false if timeout expired
95    */
96   template <typename Rep, typename Period, typename F>
97   bool try_wait_for(
98       const std::chrono::duration<Rep, Period>& timeout,
99       F&& mainContextFunc);
100
101   /**
102    * Puts active fiber to sleep. Returns when post is called or the deadline
103    * expires.
104    *
105    * @param timeout Baton will be automatically awaken if deadline expires
106    *
107    * @return true if was posted, false if timeout expired
108    */
109   template <typename Clock, typename Duration>
110   bool try_wait_until(
111       const std::chrono::time_point<Clock, Duration>& deadline) {
112     return try_wait_until(deadline, [] {});
113   }
114
115   /**
116    * Puts active fiber to sleep. Returns when post is called or the deadline
117    * expires.
118    *
119    * @param timeout Baton will be automatically awaken if deadline expires
120    * @param mainContextFunc this function is immediately executed on the main
121    *        context.
122    *
123    * @return true if was posted, false if timeout expired
124    */
125   template <typename Clock, typename Duration, typename F>
126   bool try_wait_until(
127       const std::chrono::time_point<Clock, Duration>& deadline,
128       F&& mainContextFunc);
129
130   /**
131    * Puts active fiber to sleep. Returns when post is called or the deadline
132    * expires.
133    *
134    * @param timeout Baton will be automatically awaken if deadline expires
135    * @param mainContextFunc this function is immediately executed on the main
136    *        context.
137    *
138    * @return true if was posted, false if timeout expired
139    */
140   template <typename Clock, typename Duration, typename F>
141   bool try_wait_for(
142       const std::chrono::time_point<Clock, Duration>& deadline,
143       F&& mainContextFunc);
144
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);
149   }
150
151   /// Alias to try_wait_for. Deprecated.
152   template <typename Rep, typename Period, typename F>
153   bool timed_wait(
154       const std::chrono::duration<Rep, Period>& timeout,
155       F&& mainContextFunc) {
156     return try_wait_for(timeout, static_cast<F&&>(mainContextFunc));
157   }
158
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);
163   }
164
165   /// Alias to try_wait_until. Deprecated.
166   template <typename Clock, typename Duration, typename F>
167   bool timed_wait(
168       const std::chrono::time_point<Clock, Duration>& deadline,
169       F&& mainContextFunc) {
170     return try_wait_until(deadline, static_cast<F&&>(mainContextFunc));
171   }
172
173   /**
174    * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
175    * next wait() call will return immediately).
176    */
177   void post();
178
179   /**
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.
184    */
185   void reset();
186
187   /**
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.
194    */
195   class TimeoutHandler {
196    public:
197     void scheduleTimeout(TimeoutController::Duration timeoutMs);
198
199    private:
200     friend class Baton;
201
202     void cancelTimeout();
203
204     std::function<void()> timeoutFunc_{nullptr};
205     FiberManager* fiberManager_{nullptr};
206
207     intptr_t timeoutPtr_{0};
208   };
209
210  private:
211   enum {
212     /**
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.
226      */
227     PreBlockAttempts = 300,
228   };
229
230   explicit Baton(intptr_t state) : waitingFiber_(state) {}
231
232   void postHelper(intptr_t new_value);
233   void postThread();
234   void waitThread();
235
236   template <typename F>
237   inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
238   /**
239    * Spin for "some time" (see discussion on PreBlockAttempts) waiting
240    * for a post.
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
243    */
244   bool spinWaitForEarlyPost();
245
246   bool timedWaitThread(TimeoutController::Duration timeout);
247
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;
252
253   union {
254     std::atomic<intptr_t> waitingFiber_;
255     struct {
256       folly::detail::Futex<> futex;
257       int32_t _unused_packing;
258     } futex_;
259   };
260 };
261 } // namespace fibers
262 } // namespace folly
263
264 #include <folly/fibers/Baton-inl.h>