/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include namespace folly { class EventBase; class RequestContext; /** * AsyncTimeout is used to asynchronously wait for a timeout to occur. */ class AsyncTimeout { public: typedef TimeoutManager::InternalEnum InternalEnum; /** * Create a new AsyncTimeout object, driven by the specified TimeoutManager. */ explicit AsyncTimeout(TimeoutManager* timeoutManager); explicit AsyncTimeout(EventBase* eventBase); /** * Create a new internal AsyncTimeout object. * * Internal timeouts are like regular timeouts, but will not stop the * TimeoutManager loop from exiting if the only remaining events are internal * timeouts. * * This is useful for implementing fallback timeouts to abort the * TimeoutManager loop if the other events have not been processed within a * specified time period: if the event loop takes too long the timeout will * fire and can stop the event loop. However, if all other events complete, * the event loop will exit even though the internal timeout is still * installed. */ AsyncTimeout(TimeoutManager* timeoutManager, InternalEnum internal); AsyncTimeout(EventBase* eventBase, InternalEnum internal); /** * Create a new AsyncTimeout object, not yet assigned to a TimeoutManager. * * attachEventBase() must be called prior to scheduling the timeout. */ AsyncTimeout(); AsyncTimeout(const AsyncTimeout&) = delete; AsyncTimeout& operator=(const AsyncTimeout&) = delete; /** * AsyncTimeout destructor. * * The timeout will be automatically cancelled if it is running. */ virtual ~AsyncTimeout(); /** * timeoutExpired() is invoked when the timeout period has expired. */ virtual void timeoutExpired() noexcept = 0; /** * Schedule the timeout to fire in the specified number of milliseconds. * * After the specified number of milliseconds has elapsed, timeoutExpired() * will be invoked by the TimeoutManager's main loop. * * If the timeout is already running, it will be rescheduled with the * new timeout value. * * @param milliseconds The timeout duration, in milliseconds. * * @return Returns true if the timeout was successfully scheduled, * and false if an error occurred. After an error, the timeout is * always unscheduled, even if scheduleTimeout() was just * rescheduling an existing timeout. */ bool scheduleTimeout(uint32_t milliseconds); bool scheduleTimeout(TimeoutManager::timeout_type timeout); bool scheduleTimeoutHighRes(TimeoutManager::timeout_type_high_res timeout); /** * Cancel the timeout, if it is running. */ void cancelTimeout(); /** * Returns true if the timeout is currently scheduled. */ bool isScheduled() const; /** * Attach the timeout to a TimeoutManager. * * This may only be called if the timeout is not currently attached to a * TimeoutManager (either by using the default constructor, or by calling * detachTimeoutManager()). * * This method must be invoked in the TimeoutManager's thread. * * The internal parameter specifies if this timeout should be treated as an * internal event. TimeoutManager::loop() will return when there are no more * non-internal events remaining. */ void attachTimeoutManager( TimeoutManager* timeoutManager, InternalEnum internal = InternalEnum::NORMAL); void attachEventBase( EventBase* eventBase, InternalEnum internal = InternalEnum::NORMAL); /** * Detach the timeout from its TimeoutManager. * * This may only be called when the timeout is not running. * Once detached, the timeout may not be scheduled again until it is * re-attached to a EventBase by calling attachEventBase(). * * This method must be called from the current TimeoutManager's thread. */ void detachTimeoutManager(); void detachEventBase(); const TimeoutManager* getTimeoutManager() { return timeoutManager_; } /** * Returns the internal handle to the event */ EventBaseBackendBase::Event* getEvent() { return &event_; } /** * Convenience function that wraps a function object as * an AsyncTimeout instance and returns the wrapper. * * Specially useful when using lambdas as AsyncTimeout * observers. * * Example: * * void foo(TimeoutManager &manager) { * std::atomic_bool done = false; * * auto observer = AsyncTimeout::make(manager, [&] { * std::cout << "hello, world!" << std::endl; * done = true; * }); * * observer->scheduleTimeout(std::chrono::seconds(5)); * * while (!done); // busy wait * } * * @author: Marcelo Juchem */ template static std::unique_ptr make( TimeoutManager& manager, TCallback&& callback); /** * Convenience function that wraps a function object as * an AsyncTimeout instance and returns the wrapper * after scheduling it using the given TimeoutManager. * * This is equivalent to calling `make_async_timeout` * followed by a `scheduleTimeout` on the resulting * wrapper. * * Specially useful when using lambdas as AsyncTimeout * observers. * * Example: * * void foo(TimeoutManager &manager) { * std::atomic_bool done = false; * * auto observer = AsyncTimeout::schedule( * std::chrono::seconds(5), manager, [&] { * std::cout << "hello, world!" << std::endl; * done = true; * } * ); * * while (!done); // busy wait * } * * @author: Marcelo Juchem */ template static std::unique_ptr schedule( TimeoutManager::timeout_type timeout, TimeoutManager& manager, TCallback&& callback); private: static void libeventCallback(libevent_fd_t fd, short events, void* arg); EventBaseBackendBase::Event event_; /* * Store a pointer to the TimeoutManager. We only use this * for some assert() statements, to make sure that AsyncTimeout is always * used from the correct thread. */ TimeoutManager* timeoutManager_; // Save the request context for when the timeout fires. std::shared_ptr context_; }; namespace detail { /** * Wraps a function object as an AsyncTimeout instance. * * @author: Marcelo Juchem */ template struct async_timeout_wrapper : public AsyncTimeout { template async_timeout_wrapper(TimeoutManager* manager, UCallback&& callback) : AsyncTimeout(manager), callback_(std::forward(callback)) {} void timeoutExpired() noexcept override { static_assert( noexcept(std::declval()()), "callback must be declared noexcept, e.g.: `[]() noexcept {}`"); callback_(); } private: TCallback callback_; }; } // namespace detail template std::unique_ptr AsyncTimeout::make( TimeoutManager& manager, TCallback&& callback) { return std::unique_ptr( new detail::async_timeout_wrapper::type>( std::addressof(manager), std::forward(callback))); } template std::unique_ptr AsyncTimeout::schedule( TimeoutManager::timeout_type timeout, TimeoutManager& manager, TCallback&& callback) { auto wrapper = AsyncTimeout::make(manager, std::forward(callback)); wrapper->scheduleTimeout(timeout); return wrapper; } } // namespace folly