2 * Copyright 2017 Facebook, Inc.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
23 #include <folly/io/async/TimeoutManager.h>
25 #include <folly/portability/Event.h>
27 #include <boost/noncopyable.hpp>
39 * AsyncTimeout is used to asynchronously wait for a timeout to occur.
41 class AsyncTimeout : private boost::noncopyable {
43 typedef TimeoutManager::InternalEnum InternalEnum;
46 * Create a new AsyncTimeout object, driven by the specified TimeoutManager.
48 explicit AsyncTimeout(TimeoutManager* timeoutManager);
49 explicit AsyncTimeout(EventBase* eventBase);
52 * Create a new internal AsyncTimeout object.
54 * Internal timeouts are like regular timeouts, but will not stop the
55 * TimeoutManager loop from exiting if the only remaining events are internal
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
65 AsyncTimeout(TimeoutManager* timeoutManager, InternalEnum internal);
66 AsyncTimeout(EventBase* eventBase, InternalEnum internal);
69 * Create a new AsyncTimeout object, not yet assigned to a TimeoutManager.
71 * attachEventBase() must be called prior to scheduling the timeout.
76 * AsyncTimeout destructor.
78 * The timeout will be automatically cancelled if it is running.
80 virtual ~AsyncTimeout();
83 * timeoutExpired() is invoked when the timeout period has expired.
85 virtual void timeoutExpired() noexcept = 0;
88 * Schedule the timeout to fire in the specified number of milliseconds.
90 * After the specified number of milliseconds has elapsed, timeoutExpired()
91 * will be invoked by the TimeoutManager's main loop.
93 * If the timeout is already running, it will be rescheduled with the
96 * @param milliseconds The timeout duration, in milliseconds.
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.
103 bool scheduleTimeout(uint32_t milliseconds);
104 bool scheduleTimeout(TimeoutManager::timeout_type timeout);
107 * Cancel the timeout, if it is running.
109 void cancelTimeout();
112 * Returns true if the timeout is currently scheduled.
114 bool isScheduled() const;
117 * Attach the timeout to a TimeoutManager.
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()).
123 * This method must be invoked in the TimeoutManager's thread.
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.
129 void attachTimeoutManager(TimeoutManager* timeoutManager,
130 InternalEnum internal = InternalEnum::NORMAL);
131 void attachEventBase(EventBase* eventBase,
132 InternalEnum internal = InternalEnum::NORMAL);
135 * Detach the timeout from its TimeoutManager.
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().
141 * This method must be called from the current TimeoutManager's thread.
143 void detachTimeoutManager();
144 void detachEventBase();
146 const TimeoutManager* getTimeoutManager() {
147 return timeoutManager_;
151 * Returns the internal handle to the event
153 struct event* getEvent() {
158 * Convenience function that wraps a function object as
159 * an AsyncTimeout instance and returns the wrapper.
161 * Specially useful when using lambdas as AsyncTimeout
166 * void foo(TimeoutManager &manager) {
167 * std::atomic_bool done = false;
169 * auto observer = AsyncTimeout::make(manager, [&] {
170 * std::cout << "hello, world!" << std::endl;
174 * observer->scheduleTimeout(std::chrono::seconds(5));
176 * while (!done); // busy wait
179 * @author: Marcelo Juchem <marcelo@fb.com>
181 template <typename TCallback>
182 static std::unique_ptr<AsyncTimeout> make(
183 TimeoutManager &manager,
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.
192 * This is equivalent to calling `make_async_timeout`
193 * followed by a `scheduleTimeout` on the resulting
196 * Specially useful when using lambdas as AsyncTimeout
201 * void foo(TimeoutManager &manager) {
202 * std::atomic_bool done = false;
204 * auto observer = AsyncTimeout::schedule(
205 * std::chrono::seconds(5), manager, [&] {
206 * std::cout << "hello, world!" << std::endl;
211 * while (!done); // busy wait
214 * @author: Marcelo Juchem <marcelo@fb.com>
216 template <typename TCallback>
217 static std::unique_ptr<AsyncTimeout> schedule(
218 TimeoutManager::timeout_type timeout,
219 TimeoutManager &manager,
224 static void libeventCallback(libevent_fd_t fd, short events, void* arg);
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.
233 TimeoutManager* timeoutManager_;
235 // Save the request context for when the timeout fires.
236 std::shared_ptr<RequestContext> context_;
242 * Wraps a function object as an AsyncTimeout instance.
244 * @author: Marcelo Juchem <marcelo@fb.com>
246 template <typename TCallback>
247 struct async_timeout_wrapper:
250 template <typename UCallback>
251 async_timeout_wrapper(TimeoutManager *manager, UCallback &&callback):
252 AsyncTimeout(manager),
253 callback_(std::forward<UCallback>(callback))
256 void timeoutExpired() noexcept {
258 noexcept(std::declval<TCallback>()()),
259 "callback must be declared noexcept, e.g.: `[]() noexcept {}`"
268 } // namespace detail {
270 template <typename TCallback>
271 std::unique_ptr<AsyncTimeout> AsyncTimeout::make(
272 TimeoutManager &manager,
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)
283 template <typename TCallback>
284 std::unique_ptr<AsyncTimeout> AsyncTimeout::schedule(
285 TimeoutManager::timeout_type timeout,
286 TimeoutManager &manager,
289 auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback));
290 wrapper->scheduleTimeout(timeout);