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