fix flaky ConnectTFOTimeout and ConnectTFOFallbackTimeout tests
[folly.git] / folly / io / async / AsyncTimeout.h
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements. See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership. The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License. You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied. See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21 #pragma once
22
23 #include <folly/io/async/TimeoutManager.h>
24
25 #include <folly/portability/Event.h>
26
27 #include <boost/noncopyable.hpp>
28 #include <event.h>
29 #include <memory>
30 #include <utility>
31
32 namespace folly {
33
34 class EventBase;
35 class RequestContext;
36 class TimeoutManager;
37
38 /**
39  * AsyncTimeout is used to asynchronously wait for a timeout to occur.
40  */
41 class AsyncTimeout : private boost::noncopyable {
42  public:
43   typedef TimeoutManager::InternalEnum InternalEnum;
44
45   /**
46    * Create a new AsyncTimeout object, driven by the specified TimeoutManager.
47    */
48   explicit AsyncTimeout(TimeoutManager* timeoutManager);
49   explicit AsyncTimeout(EventBase* eventBase);
50
51   /**
52    * Create a new internal AsyncTimeout object.
53    *
54    * Internal timeouts are like regular timeouts, but will not stop the
55    * TimeoutManager loop from exiting if the only remaining events are internal
56    * timeouts.
57    *
58    * This is useful for implementing fallback timeouts to abort the
59    * TimeoutManager loop if the other events have not been processed within a
60    * specified time period: if the event loop takes too long the timeout will
61    * fire and can stop the event loop.  However, if all other events complete,
62    * the event loop will exit even though the internal timeout is still
63    * installed.
64    */
65   AsyncTimeout(TimeoutManager* timeoutManager, InternalEnum internal);
66   AsyncTimeout(EventBase* eventBase, InternalEnum internal);
67
68   /**
69    * Create a new AsyncTimeout object, not yet assigned to a TimeoutManager.
70    *
71    * attachEventBase() must be called prior to scheduling the timeout.
72    */
73   AsyncTimeout();
74
75   /**
76    * AsyncTimeout destructor.
77    *
78    * The timeout will be automatically cancelled if it is running.
79    */
80   virtual ~AsyncTimeout();
81
82   /**
83    * timeoutExpired() is invoked when the timeout period has expired.
84    */
85   virtual void timeoutExpired() noexcept = 0;
86
87   /**
88    * Schedule the timeout to fire in the specified number of milliseconds.
89    *
90    * After the specified number of milliseconds has elapsed, timeoutExpired()
91    * will be invoked by the TimeoutManager's main loop.
92    *
93    * If the timeout is already running, it will be rescheduled with the
94    * new timeout value.
95    *
96    * @param milliseconds  The timeout duration, in milliseconds.
97    *
98    * @return Returns true if the timeout was successfully scheduled,
99    *         and false if an error occurred.  After an error, the timeout is
100    *         always unscheduled, even if scheduleTimeout() was just
101    *         rescheduling an existing timeout.
102    */
103   bool scheduleTimeout(uint32_t milliseconds);
104   bool scheduleTimeout(TimeoutManager::timeout_type timeout);
105
106   /**
107    * Cancel the timeout, if it is running.
108    */
109   void cancelTimeout();
110
111   /**
112    * Returns true if the timeout is currently scheduled.
113    */
114   bool isScheduled() const;
115
116   /**
117    * Attach the timeout to a TimeoutManager.
118    *
119    * This may only be called if the timeout is not currently attached to a
120    * TimeoutManager (either by using the default constructor, or by calling
121    * detachTimeoutManager()).
122    *
123    * This method must be invoked in the TimeoutManager's thread.
124    *
125    * The internal parameter specifies if this timeout should be treated as an
126    * internal event.  TimeoutManager::loop() will return when there are no more
127    * non-internal events remaining.
128    */
129   void attachTimeoutManager(TimeoutManager* timeoutManager,
130                             InternalEnum internal = InternalEnum::NORMAL);
131   void attachEventBase(EventBase* eventBase,
132                        InternalEnum internal = InternalEnum::NORMAL);
133
134   /**
135    * Detach the timeout from its TimeoutManager.
136    *
137    * This may only be called when the timeout is not running.
138    * Once detached, the timeout may not be scheduled again until it is
139    * re-attached to a EventBase by calling attachEventBase().
140    *
141    * This method must be called from the current TimeoutManager's thread.
142    */
143   void detachTimeoutManager();
144   void detachEventBase();
145
146   const TimeoutManager* getTimeoutManager() {
147     return timeoutManager_;
148   }
149
150   /**
151    * Returns the internal handle to the event
152    */
153   struct event* getEvent() {
154     return &event_;
155   }
156
157   /**
158    * Convenience function that wraps a function object as
159    * an AsyncTimeout instance and returns the wrapper.
160    *
161    * Specially useful when using lambdas as AsyncTimeout
162    * observers.
163    *
164    * Example:
165    *
166    *  void foo(TimeoutManager &manager) {
167    *    std::atomic_bool done = false;
168    *
169    *    auto observer = AsyncTimeout::make(manager, [&] {
170    *      std::cout << "hello, world!" << std::endl;
171    *      done = true;
172    *    });
173    *
174    *    observer->scheduleTimeout(std::chrono::seconds(5));
175    *
176    *    while (!done); // busy wait
177    *  }
178    *
179    * @author: Marcelo Juchem <marcelo@fb.com>
180    */
181   template <typename TCallback>
182   static std::unique_ptr<AsyncTimeout> make(
183     TimeoutManager &manager,
184     TCallback &&callback
185   );
186
187   /**
188    * Convenience function that wraps a function object as
189    * an AsyncTimeout instance and returns the wrapper
190    * after scheduling it using the given TimeoutManager.
191    *
192    * This is equivalent to calling `make_async_timeout`
193    * followed by a `scheduleTimeout` on the resulting
194    * wrapper.
195    *
196    * Specially useful when using lambdas as AsyncTimeout
197    * observers.
198    *
199    * Example:
200    *
201    *  void foo(TimeoutManager &manager) {
202    *    std::atomic_bool done = false;
203    *
204    *    auto observer = AsyncTimeout::schedule(
205    *      std::chrono::seconds(5), manager, [&] {
206    *        std::cout << "hello, world!" << std::endl;
207    *        done = true;
208    *      }
209    *    );
210    *
211    *    while (!done); // busy wait
212    *  }
213    *
214    * @author: Marcelo Juchem <marcelo@fb.com>
215    */
216   template <typename TCallback>
217   static std::unique_ptr<AsyncTimeout> schedule(
218     TimeoutManager::timeout_type timeout,
219     TimeoutManager &manager,
220     TCallback &&callback
221   );
222
223  private:
224   static void libeventCallback(libevent_fd_t fd, short events, void* arg);
225
226   struct event event_;
227
228   /*
229    * Store a pointer to the TimeoutManager.  We only use this
230    * for some assert() statements, to make sure that AsyncTimeout is always
231    * used from the correct thread.
232    */
233   TimeoutManager* timeoutManager_;
234
235   // Save the request context for when the timeout fires.
236   std::shared_ptr<RequestContext> context_;
237 };
238
239 namespace detail {
240
241 /**
242  * Wraps a function object as an AsyncTimeout instance.
243  *
244  * @author: Marcelo Juchem <marcelo@fb.com>
245  */
246 template <typename TCallback>
247 struct async_timeout_wrapper:
248   public AsyncTimeout
249 {
250   template <typename UCallback>
251   async_timeout_wrapper(TimeoutManager *manager, UCallback &&callback):
252     AsyncTimeout(manager),
253     callback_(std::forward<UCallback>(callback))
254   {}
255
256   void timeoutExpired() noexcept {
257     static_assert(
258       noexcept(std::declval<TCallback>()()),
259       "callback must be declared noexcept, e.g.: `[]() noexcept {}`"
260     );
261     callback_();
262   }
263
264 private:
265   TCallback callback_;
266 };
267
268 } // namespace detail {
269
270 template <typename TCallback>
271 std::unique_ptr<AsyncTimeout> AsyncTimeout::make(
272   TimeoutManager &manager,
273   TCallback &&callback
274 ) {
275   return std::unique_ptr<AsyncTimeout>(
276     new detail::async_timeout_wrapper<typename std::decay<TCallback>::type>(
277       std::addressof(manager),
278       std::forward<TCallback>(callback)
279     )
280   );
281 }
282
283 template <typename TCallback>
284 std::unique_ptr<AsyncTimeout> AsyncTimeout::schedule(
285   TimeoutManager::timeout_type timeout,
286   TimeoutManager &manager,
287   TCallback &&callback
288 ) {
289   auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback));
290   wrapper->scheduleTimeout(timeout);
291   return wrapper;
292 }
293
294 } // folly