36070ce78eb8ff87b18ba3fd7403f4f9b3c9aeca
[folly.git] / folly / fibers / Baton.h
1 /*
2  * Copyright 2017 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    * This is here only not break tao/locks. Please don't use it, because it is
68    * inefficient when used on Fibers.
69    */
70   template <typename C, typename D = typename C::duration>
71   bool timed_wait(const std::chrono::time_point<C, D>& timeout);
72
73   /**
74    * Puts active fiber to sleep. Returns when post is called.
75    *
76    * @param timeout Baton will be automatically awaken if timeout is hit
77    *
78    * @return true if was posted, false if timeout expired
79    */
80   bool timed_wait(TimeoutController::Duration timeout);
81
82   /**
83    * Puts active fiber to sleep. Returns when post is called.
84    *
85    * @param timeout Baton will be automatically awaken if timeout is hit
86    * @param mainContextFunc this function is immediately executed on the main
87    *        context.
88    *
89    * @return true if was posted, false if timeout expired
90    */
91   template <typename F>
92   bool timed_wait(TimeoutController::Duration timeout, F&& mainContextFunc);
93
94   /**
95    * Checks if the baton has been posted without blocking.
96    * @return    true iff the baton has been posted.
97    */
98   bool try_wait();
99
100   /**
101    * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
102    * next wait() call will return immediately).
103    */
104   void post();
105
106   /**
107    * Reset's the baton (equivalent to destroying the object and constructing
108    * another one in place).
109    * Caller is responsible for making sure no one is waiting on/posting the
110    * baton when reset() is called.
111    */
112   void reset();
113
114   /**
115    * Provides a way to schedule a wakeup for a wait()ing fiber.
116    * A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)
117    * before a timeout is scheduled. It is only safe to use the
118    * TimeoutHandler on the same thread as the wait()ing fiber.
119    * scheduleTimeout() may only be called once prior to the end of the
120    * associated Baton's life.
121    */
122   class TimeoutHandler {
123    public:
124     void scheduleTimeout(TimeoutController::Duration timeoutMs);
125
126    private:
127     friend class Baton;
128
129     void cancelTimeout();
130
131     std::function<void()> timeoutFunc_{nullptr};
132     FiberManager* fiberManager_{nullptr};
133
134     intptr_t timeoutPtr_{0};
135   };
136
137  private:
138   enum {
139     /**
140      * Must be positive.  If multiple threads are actively using a
141      * higher-level data structure that uses batons internally, it is
142      * likely that the post() and wait() calls happen almost at the same
143      * time.  In this state, we lose big 50% of the time if the wait goes
144      * to sleep immediately.  On circa-2013 devbox hardware it costs about
145      * 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
146      * posix_sem_pingpong test in BatonTests).  We can improve our chances
147      * of early post by spinning for a bit, although we have to balance
148      * this against the loss if we end up sleeping any way.  Spins on this
149      * hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
150      * We give ourself 300 spins, which is about 2 usec of waiting.  As a
151      * partial consolation, since we are using the pause instruction we
152      * are giving a speed boost to the colocated hyperthread.
153      */
154     PreBlockAttempts = 300,
155   };
156
157   explicit Baton(intptr_t state) : waitingFiber_(state){}
158
159   void postHelper(intptr_t new_value);
160   void postThread();
161   void waitThread();
162
163   template <typename F>
164   inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
165   /**
166    * Spin for "some time" (see discussion on PreBlockAttempts) waiting
167    * for a post.
168    * @return true if we received a post the spin wait, false otherwise. If the
169    *         function returns true then Baton state is guaranteed to be POSTED
170    */
171   bool spinWaitForEarlyPost();
172
173   bool timedWaitThread(TimeoutController::Duration timeout);
174
175   static constexpr intptr_t NO_WAITER = 0;
176   static constexpr intptr_t POSTED = -1;
177   static constexpr intptr_t TIMEOUT = -2;
178   static constexpr intptr_t THREAD_WAITING = -3;
179
180   union {
181     std::atomic<intptr_t> waitingFiber_;
182     struct {
183       folly::detail::Futex<> futex;
184       int32_t _unused_packing;
185     } futex_;
186   };
187 };
188 }
189 }
190
191 #include <folly/fibers/Baton-inl.h>