From e4c703f058efa86e16ea11ff8471220b3867c2ca Mon Sep 17 00:00:00 2001 From: Marcelo Juchem Date: Thu, 30 Apr 2015 14:03:16 -0700 Subject: [PATCH] Convenience functions to wrap a lambda as an AsyncTimeout. Summary: see title Test Plan: unit tests added Reviewed By: davejwatson@fb.com Subscribers: folly-diffs@, yfeldblum, chalfant, andrewcox FB internal diff: D2000579 Signature: t1:2000579:1430345677:1d7a78f94bcd8b0912423ca4987a4048c103241c --- folly/io/async/AsyncTimeout.cpp | 4 +- folly/io/async/AsyncTimeout.h | 124 ++++++++++++++++++++++- folly/io/async/EventBase.cpp | 2 +- folly/io/async/EventBase.h | 2 +- folly/io/async/TimeoutManager.h | 4 +- folly/io/async/test/AsyncTimeoutTest.cpp | 93 +++++++++++++++++ 6 files changed, 223 insertions(+), 6 deletions(-) create mode 100644 folly/io/async/test/AsyncTimeoutTest.cpp diff --git a/folly/io/async/AsyncTimeout.cpp b/folly/io/async/AsyncTimeout.cpp index 4d120152..0b371d77 100644 --- a/folly/io/async/AsyncTimeout.cpp +++ b/folly/io/async/AsyncTimeout.cpp @@ -81,14 +81,14 @@ AsyncTimeout::~AsyncTimeout() { cancelTimeout(); } -bool AsyncTimeout::scheduleTimeout(std::chrono::milliseconds timeout) { +bool AsyncTimeout::scheduleTimeout(TimeoutManager::timeout_type timeout) { assert(timeoutManager_ != nullptr); context_ = RequestContext::saveContext(); return timeoutManager_->scheduleTimeout(this, timeout); } bool AsyncTimeout::scheduleTimeout(uint32_t milliseconds) { - return scheduleTimeout(std::chrono::milliseconds(milliseconds)); + return scheduleTimeout(TimeoutManager::timeout_type(milliseconds)); } void AsyncTimeout::cancelTimeout() { diff --git a/folly/io/async/AsyncTimeout.h b/folly/io/async/AsyncTimeout.h index 6107ce52..189fcb19 100644 --- a/folly/io/async/AsyncTimeout.h +++ b/folly/io/async/AsyncTimeout.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace folly { @@ -98,7 +99,7 @@ class AsyncTimeout : private boost::noncopyable { * rescheduling an existing timeout. */ bool scheduleTimeout(uint32_t milliseconds); - bool scheduleTimeout(std::chrono::milliseconds timeout); + bool scheduleTimeout(TimeoutManager::timeout_type timeout); /** * Cancel the timeout, if it is running. @@ -151,6 +152,72 @@ class AsyncTimeout : private boost::noncopyable { 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(int fd, short events, void* arg); @@ -167,4 +234,59 @@ class AsyncTimeout : private boost::noncopyable { 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 { + 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; +} + } // folly diff --git a/folly/io/async/EventBase.cpp b/folly/io/async/EventBase.cpp index b6cd8ca1..1c3ad397 100644 --- a/folly/io/async/EventBase.cpp +++ b/folly/io/async/EventBase.cpp @@ -781,7 +781,7 @@ void EventBase::detachTimeoutManager(AsyncTimeout* obj) { } bool EventBase::scheduleTimeout(AsyncTimeout* obj, - std::chrono::milliseconds timeout) { + TimeoutManager::timeout_type timeout) { assert(isInEventBaseThread()); // Set up the timeval and add the event struct timeval tv; diff --git a/folly/io/async/EventBase.h b/folly/io/async/EventBase.h index 0bdc428a..3e28d294 100644 --- a/folly/io/async/EventBase.h +++ b/folly/io/async/EventBase.h @@ -575,7 +575,7 @@ bool runImmediatelyOrRunInEventBaseThreadAndWait(const Cob& fn); void detachTimeoutManager(AsyncTimeout* obj) override; - bool scheduleTimeout(AsyncTimeout* obj, std::chrono::milliseconds timeout) + bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout) override; void cancelTimeout(AsyncTimeout* obj) override; diff --git a/folly/io/async/TimeoutManager.h b/folly/io/async/TimeoutManager.h index 16042807..2b35574c 100644 --- a/folly/io/async/TimeoutManager.h +++ b/folly/io/async/TimeoutManager.h @@ -34,6 +34,8 @@ class AsyncTimeout; */ class TimeoutManager { public: + typedef std::chrono::milliseconds timeout_type; + enum class InternalEnum { INTERNAL, NORMAL @@ -52,7 +54,7 @@ class TimeoutManager { * Schedules AsyncTimeout to fire after `timeout` milliseconds */ virtual bool scheduleTimeout(AsyncTimeout* obj, - std::chrono::milliseconds timeout) = 0; + timeout_type timeout) = 0; /** * Cancels the AsyncTimeout, if scheduled diff --git a/folly/io/async/test/AsyncTimeoutTest.cpp b/folly/io/async/test/AsyncTimeoutTest.cpp new file mode 100644 index 00000000..ff5cd1cc --- /dev/null +++ b/folly/io/async/test/AsyncTimeoutTest.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * 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. + */ + +#include +#include + +#include + +namespace folly { + +TEST(AsyncTimeout, make) { + int value = 0; + int const expected = 10; + EventBase manager; + + auto observer = AsyncTimeout::make( + manager, + [&]() noexcept { value = expected; } + ); + + observer->scheduleTimeout(std::chrono::milliseconds(100)); + + manager.loop(); + + EXPECT_EQ(expected, value); +} + +TEST(AsyncTimeout, schedule) { + int value = 0; + int const expected = 10; + EventBase manager; + + auto observer = AsyncTimeout::schedule( + std::chrono::milliseconds(100), + manager, + [&]() noexcept { value = expected; } + ); + + manager.loop(); + + EXPECT_EQ(expected, value); +} + +TEST(AsyncTimeout, cancel_make) { + int value = 0; + int const expected = 10; + EventBase manager; + + auto observer = AsyncTimeout::make( + manager, + [&]() noexcept { value = expected; } + ); + + observer->scheduleTimeout(std::chrono::milliseconds(100)); + observer->cancelTimeout(); + + manager.loop(); + + EXPECT_NE(expected, value); +} + +TEST(AsyncTimeout, cancel_schedule) { + int value = 0; + int const expected = 10; + EventBase manager; + + auto observer = AsyncTimeout::schedule( + std::chrono::milliseconds(100), + manager, + [&]() noexcept { value = expected; } + ); + + observer->cancelTimeout(); + + manager.loop(); + + EXPECT_NE(expected, value); +} + +} // namespace folly { -- 2.34.1