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