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() {
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() {
......
......@@ -25,6 +25,7 @@
#include <boost/noncopyable.hpp>
#include <event.h>
#include <memory>
#include <utility>
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 <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:
static void libeventCallback(int fd, short events, void* arg);
......@@ -167,4 +234,59 @@ class AsyncTimeout : private boost::noncopyable {
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
......@@ -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;
......
......@@ -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;
......
......@@ -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
......
/*
* 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