Commit e4c703f0 authored by Marcelo Juchem's avatar Marcelo Juchem Committed by Praveen Kumar Ramakrishnan

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
parent 7f413ec3
...@@ -81,14 +81,14 @@ AsyncTimeout::~AsyncTimeout() { ...@@ -81,14 +81,14 @@ AsyncTimeout::~AsyncTimeout() {
cancelTimeout(); cancelTimeout();
} }
bool AsyncTimeout::scheduleTimeout(std::chrono::milliseconds timeout) { bool AsyncTimeout::scheduleTimeout(TimeoutManager::timeout_type timeout) {
assert(timeoutManager_ != nullptr); assert(timeoutManager_ != nullptr);
context_ = RequestContext::saveContext(); context_ = RequestContext::saveContext();
return timeoutManager_->scheduleTimeout(this, timeout); return timeoutManager_->scheduleTimeout(this, timeout);
} }
bool AsyncTimeout::scheduleTimeout(uint32_t milliseconds) { bool AsyncTimeout::scheduleTimeout(uint32_t milliseconds) {
return scheduleTimeout(std::chrono::milliseconds(milliseconds)); return scheduleTimeout(TimeoutManager::timeout_type(milliseconds));
} }
void AsyncTimeout::cancelTimeout() { void AsyncTimeout::cancelTimeout() {
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <event.h> #include <event.h>
#include <memory> #include <memory>
#include <utility>
namespace folly { namespace folly {
...@@ -98,7 +99,7 @@ class AsyncTimeout : private boost::noncopyable { ...@@ -98,7 +99,7 @@ class AsyncTimeout : private boost::noncopyable {
* rescheduling an existing timeout. * rescheduling an existing timeout.
*/ */
bool scheduleTimeout(uint32_t milliseconds); bool scheduleTimeout(uint32_t milliseconds);
bool scheduleTimeout(std::chrono::milliseconds timeout); bool scheduleTimeout(TimeoutManager::timeout_type timeout);
/** /**
* Cancel the timeout, if it is running. * Cancel the timeout, if it is running.
...@@ -151,6 +152,72 @@ class AsyncTimeout : private boost::noncopyable { ...@@ -151,6 +152,72 @@ class AsyncTimeout : private boost::noncopyable {
return &event_; 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 <marcelo@fb.com>
*/
template <typename TCallback>
static std::unique_ptr<AsyncTimeout> 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 <marcelo@fb.com>
*/
template <typename TCallback>
static std::unique_ptr<AsyncTimeout> schedule(
TimeoutManager::timeout_type timeout,
TimeoutManager &manager,
TCallback &&callback
);
private: private:
static void libeventCallback(int fd, short events, void* arg); static void libeventCallback(int fd, short events, void* arg);
...@@ -167,4 +234,59 @@ class AsyncTimeout : private boost::noncopyable { ...@@ -167,4 +234,59 @@ class AsyncTimeout : private boost::noncopyable {
std::shared_ptr<RequestContext> context_; std::shared_ptr<RequestContext> context_;
}; };
namespace detail {
/**
* Wraps a function object as an AsyncTimeout instance.
*
* @author: Marcelo Juchem <marcelo@fb.com>
*/
template <typename TCallback>
struct async_timeout_wrapper:
public AsyncTimeout
{
template <typename UCallback>
async_timeout_wrapper(TimeoutManager *manager, UCallback &&callback):
AsyncTimeout(manager),
callback_(std::forward<UCallback>(callback))
{}
void timeoutExpired() noexcept {
static_assert(
noexcept(std::declval<TCallback>()()),
"callback must be declared noexcept, e.g.: `[]() noexcept {}`"
);
callback_();
}
private:
TCallback callback_;
};
} // namespace detail {
template <typename TCallback>
std::unique_ptr<AsyncTimeout> AsyncTimeout::make(
TimeoutManager &manager,
TCallback &&callback
) {
return std::unique_ptr<AsyncTimeout>(
new detail::async_timeout_wrapper<typename std::decay<TCallback>::type>(
std::addressof(manager),
std::forward<TCallback>(callback)
)
);
}
template <typename TCallback>
std::unique_ptr<AsyncTimeout> AsyncTimeout::schedule(
TimeoutManager::timeout_type timeout,
TimeoutManager &manager,
TCallback &&callback
) {
auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback));
wrapper->scheduleTimeout(timeout);
return wrapper;
}
} // folly } // folly
...@@ -781,7 +781,7 @@ void EventBase::detachTimeoutManager(AsyncTimeout* obj) { ...@@ -781,7 +781,7 @@ void EventBase::detachTimeoutManager(AsyncTimeout* obj) {
} }
bool EventBase::scheduleTimeout(AsyncTimeout* obj, bool EventBase::scheduleTimeout(AsyncTimeout* obj,
std::chrono::milliseconds timeout) { TimeoutManager::timeout_type timeout) {
assert(isInEventBaseThread()); assert(isInEventBaseThread());
// Set up the timeval and add the event // Set up the timeval and add the event
struct timeval tv; struct timeval tv;
......
...@@ -575,7 +575,7 @@ bool runImmediatelyOrRunInEventBaseThreadAndWait(const Cob& fn); ...@@ -575,7 +575,7 @@ bool runImmediatelyOrRunInEventBaseThreadAndWait(const Cob& fn);
void detachTimeoutManager(AsyncTimeout* obj) override; void detachTimeoutManager(AsyncTimeout* obj) override;
bool scheduleTimeout(AsyncTimeout* obj, std::chrono::milliseconds timeout) bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout)
override; override;
void cancelTimeout(AsyncTimeout* obj) override; void cancelTimeout(AsyncTimeout* obj) override;
......
...@@ -34,6 +34,8 @@ class AsyncTimeout; ...@@ -34,6 +34,8 @@ class AsyncTimeout;
*/ */
class TimeoutManager { class TimeoutManager {
public: public:
typedef std::chrono::milliseconds timeout_type;
enum class InternalEnum { enum class InternalEnum {
INTERNAL, INTERNAL,
NORMAL NORMAL
...@@ -52,7 +54,7 @@ class TimeoutManager { ...@@ -52,7 +54,7 @@ class TimeoutManager {
* Schedules AsyncTimeout to fire after `timeout` milliseconds * Schedules AsyncTimeout to fire after `timeout` milliseconds
*/ */
virtual bool scheduleTimeout(AsyncTimeout* obj, virtual bool scheduleTimeout(AsyncTimeout* obj,
std::chrono::milliseconds timeout) = 0; timeout_type timeout) = 0;
/** /**
* Cancels the AsyncTimeout, if scheduled * Cancels the AsyncTimeout, if scheduled
......
/*
* 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 <folly/io/async/AsyncTimeout.h>
#include <folly/io/async/EventBase.h>
#include <gtest/gtest.h>
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 {
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment