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