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>
38 * AsyncTimeout is used to asynchronously wait for a timeout to occur.
40 class AsyncTimeout : private boost::noncopyable {
42 typedef TimeoutManager::InternalEnum InternalEnum;
45 * Create a new AsyncTimeout object, driven by the specified TimeoutManager.
47 explicit AsyncTimeout(TimeoutManager* timeoutManager);
48 explicit AsyncTimeout(EventBase* eventBase);
51 * Create a new internal AsyncTimeout object.
53 * Internal timeouts are like regular timeouts, but will not stop the
54 * TimeoutManager loop from exiting if the only remaining events are internal
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
64 AsyncTimeout(TimeoutManager* timeoutManager, InternalEnum internal);
65 AsyncTimeout(EventBase* eventBase, InternalEnum internal);
68 * Create a new AsyncTimeout object, not yet assigned to a TimeoutManager.
70 * attachEventBase() must be called prior to scheduling the timeout.
75 * AsyncTimeout destructor.
77 * The timeout will be automatically cancelled if it is running.
79 virtual ~AsyncTimeout();
82 * timeoutExpired() is invoked when the timeout period has expired.
84 virtual void timeoutExpired() noexcept = 0;
87 * Schedule the timeout to fire in the specified number of milliseconds.
89 * After the specified number of milliseconds has elapsed, timeoutExpired()
90 * will be invoked by the TimeoutManager's main loop.
92 * If the timeout is already running, it will be rescheduled with the
95 * @param milliseconds The timeout duration, in milliseconds.
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.
102 bool scheduleTimeout(uint32_t milliseconds);
103 bool scheduleTimeout(TimeoutManager::timeout_type timeout);
106 * Cancel the timeout, if it is running.
108 void cancelTimeout();
111 * Returns true if the timeout is currently scheduled.
113 bool isScheduled() const;
116 * Attach the timeout to a TimeoutManager.
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()).
122 * This method must be invoked in the TimeoutManager's thread.
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.
128 void attachTimeoutManager(TimeoutManager* timeoutManager,
129 InternalEnum internal = InternalEnum::NORMAL);
130 void attachEventBase(EventBase* eventBase,
131 InternalEnum internal = InternalEnum::NORMAL);
134 * Detach the timeout from its TimeoutManager.
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().
140 * This method must be called from the current TimeoutManager's thread.
142 void detachTimeoutManager();
143 void detachEventBase();
145 const TimeoutManager* getTimeoutManager() {
146 return timeoutManager_;
150 * Returns the internal handle to the event
152 struct event* getEvent() {
157 * Convenience function that wraps a function object as
158 * an AsyncTimeout instance and returns the wrapper.
160 * Specially useful when using lambdas as AsyncTimeout
165 * void foo(TimeoutManager &manager) {
166 * std::atomic_bool done = false;
168 * auto observer = AsyncTimeout::make(manager, [&] {
169 * std::cout << "hello, world!" << std::endl;
173 * observer->scheduleTimeout(std::chrono::seconds(5));
175 * while (!done); // busy wait
178 * @author: Marcelo Juchem <marcelo@fb.com>
180 template <typename TCallback>
181 static std::unique_ptr<AsyncTimeout> make(
182 TimeoutManager &manager,
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.
191 * This is equivalent to calling `make_async_timeout`
192 * followed by a `scheduleTimeout` on the resulting
195 * Specially useful when using lambdas as AsyncTimeout
200 * void foo(TimeoutManager &manager) {
201 * std::atomic_bool done = false;
203 * auto observer = AsyncTimeout::schedule(
204 * std::chrono::seconds(5), manager, [&] {
205 * std::cout << "hello, world!" << std::endl;
210 * while (!done); // busy wait
213 * @author: Marcelo Juchem <marcelo@fb.com>
215 template <typename TCallback>
216 static std::unique_ptr<AsyncTimeout> schedule(
217 TimeoutManager::timeout_type timeout,
218 TimeoutManager &manager,
223 static void libeventCallback(libevent_fd_t fd, short events, void* arg);
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.
232 TimeoutManager* timeoutManager_;
234 // Save the request context for when the timeout fires.
235 std::shared_ptr<RequestContext> context_;
241 * Wraps a function object as an AsyncTimeout instance.
243 * @author: Marcelo Juchem <marcelo@fb.com>
245 template <typename TCallback>
246 struct async_timeout_wrapper:
249 template <typename UCallback>
250 async_timeout_wrapper(TimeoutManager *manager, UCallback &&callback):
251 AsyncTimeout(manager),
252 callback_(std::forward<UCallback>(callback))
255 void timeoutExpired() noexcept {
257 noexcept(std::declval<TCallback>()()),
258 "callback must be declared noexcept, e.g.: `[]() noexcept {}`"
267 } // namespace detail {
269 template <typename TCallback>
270 std::unique_ptr<AsyncTimeout> AsyncTimeout::make(
271 TimeoutManager &manager,
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)
282 template <typename TCallback>
283 std::unique_ptr<AsyncTimeout> AsyncTimeout::schedule(
284 TimeoutManager::timeout_type timeout,
285 TimeoutManager &manager,
288 auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback));
289 wrapper->scheduleTimeout(timeout);