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