Fix copyright lines
[folly.git] / folly / fibers / Baton.h
1 /*
2  * Copyright 2014-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   bool ready() const {
44     auto state = waitingFiber_.load();
45     return state == POSTED;
46   }
47
48   /**
49    * Puts active fiber to sleep. Returns when post is called.
50    */
51   void wait();
52
53   /**
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.
59    */
60   void wait(TimeoutHandler& timeoutHandler);
61
62   /**
63    * Puts active fiber to sleep. Returns when post is called.
64    *
65    * @param mainContextFunc this function is immediately executed on the main
66    *        context.
67    */
68   template <typename F>
69   void wait(F&& mainContextFunc);
70
71   /**
72    * Checks if the baton has been posted without blocking.
73    *
74    * @return    true iff the baton has been posted.
75    */
76   bool try_wait();
77
78   /**
79    * Puts active fiber to sleep. Returns when post is called or the timeout
80    * expires.
81    *
82    * @param timeout Baton will be automatically awaken if timeout expires
83    *
84    * @return true if was posted, false if timeout expired
85    */
86   template <typename Rep, typename Period>
87   bool try_wait_for(const std::chrono::duration<Rep, Period>& timeout) {
88     return try_wait_for(timeout, [] {});
89   }
90
91   /**
92    * Puts active fiber to sleep. Returns when post is called or the timeout
93    * expires.
94    *
95    * @param timeout Baton will be automatically awaken if timeout expires
96    * @param mainContextFunc this function is immediately executed on the main
97    *        context.
98    *
99    * @return true if was posted, false if timeout expired
100    */
101   template <typename Rep, typename Period, typename F>
102   bool try_wait_for(
103       const std::chrono::duration<Rep, Period>& timeout,
104       F&& mainContextFunc);
105
106   /**
107    * Puts active fiber to sleep. Returns when post is called or the deadline
108    * expires.
109    *
110    * @param timeout Baton will be automatically awaken if deadline expires
111    *
112    * @return true if was posted, false if timeout expired
113    */
114   template <typename Clock, typename Duration>
115   bool try_wait_until(
116       const std::chrono::time_point<Clock, Duration>& deadline) {
117     return try_wait_until(deadline, [] {});
118   }
119
120   /**
121    * Puts active fiber to sleep. Returns when post is called or the deadline
122    * expires.
123    *
124    * @param timeout Baton will be automatically awaken if deadline expires
125    * @param mainContextFunc this function is immediately executed on the main
126    *        context.
127    *
128    * @return true if was posted, false if timeout expired
129    */
130   template <typename Clock, typename Duration, typename F>
131   bool try_wait_until(
132       const std::chrono::time_point<Clock, Duration>& deadline,
133       F&& mainContextFunc);
134
135   /**
136    * Puts active fiber to sleep. Returns when post is called or the deadline
137    * expires.
138    *
139    * @param timeout Baton will be automatically awaken if deadline expires
140    * @param mainContextFunc this function is immediately executed on the main
141    *        context.
142    *
143    * @return true if was posted, false if timeout expired
144    */
145   template <typename Clock, typename Duration, typename F>
146   bool try_wait_for(
147       const std::chrono::time_point<Clock, Duration>& deadline,
148       F&& mainContextFunc);
149
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);
154   }
155
156   /// Alias to try_wait_for. Deprecated.
157   template <typename Rep, typename Period, typename F>
158   bool timed_wait(
159       const std::chrono::duration<Rep, Period>& timeout,
160       F&& mainContextFunc) {
161     return try_wait_for(timeout, static_cast<F&&>(mainContextFunc));
162   }
163
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);
168   }
169
170   /// Alias to try_wait_until. Deprecated.
171   template <typename Clock, typename Duration, typename F>
172   bool timed_wait(
173       const std::chrono::time_point<Clock, Duration>& deadline,
174       F&& mainContextFunc) {
175     return try_wait_until(deadline, static_cast<F&&>(mainContextFunc));
176   }
177
178   /**
179    * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
180    * next wait() call will return immediately).
181    */
182   void post();
183
184   /**
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.
189    */
190   void reset();
191
192   /**
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.
199    */
200   class TimeoutHandler {
201    public:
202     void scheduleTimeout(TimeoutController::Duration timeoutMs);
203
204    private:
205     friend class Baton;
206
207     void cancelTimeout();
208
209     std::function<void()> timeoutFunc_{nullptr};
210     FiberManager* fiberManager_{nullptr};
211
212     intptr_t timeoutPtr_{0};
213   };
214
215  private:
216   enum {
217     /**
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.
231      */
232     PreBlockAttempts = 300,
233   };
234
235   explicit Baton(intptr_t state) : waitingFiber_(state) {}
236
237   void postHelper(intptr_t new_value);
238   void postThread();
239   void waitThread();
240
241   template <typename F>
242   inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
243   /**
244    * Spin for "some time" (see discussion on PreBlockAttempts) waiting
245    * for a post.
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
248    */
249   bool spinWaitForEarlyPost();
250
251   bool timedWaitThread(TimeoutController::Duration timeout);
252
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;
257
258   union {
259     std::atomic<intptr_t> waitingFiber_;
260     struct {
261       folly::detail::Futex<> futex{};
262       int32_t _unused_packing;
263     } futex_;
264   };
265 };
266 } // namespace fibers
267 } // namespace folly
268
269 #include <folly/fibers/Baton-inl.h>