Commit 4950f56d authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Add getVia, getTryVia and waitVia with durations to SemiFuture API.

Summary: Fleshes out the SemiFuture API with waiting versions of xVia calls that take a TimedDrivableExecutor that can be driven until some time point.

Reviewed By: yfeldblum

Differential Revision: D6658862

fbshipit-source-id: 74906d8101d2a762a27da1a1ec8348f106037f5d
parent ad1abbab
......@@ -1409,15 +1409,18 @@ void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
if (f.isReady()) {
return;
}
f = f.via(e).then([](T&& t) { return std::move(t); });
f = std::move(f).via(e).then([](T&& t) { return std::move(t); });
while (!f.isReady()) {
e->drive();
}
assert(f.isReady());
}
template <class T>
void waitViaImpl(SemiFuture<T>& f, DrivableExecutor* e) {
template <class T, typename Rep, typename Period>
void waitViaImpl(
Future<T>& f,
TimedDrivableExecutor* e,
const std::chrono::duration<Rep, Period>& timeout) {
// Set callback so to ensure that the via executor has something on it
// so that once the preceding future triggers this callback, drive will
// always have a callback to satisfy it
......@@ -1425,10 +1428,13 @@ void waitViaImpl(SemiFuture<T>& f, DrivableExecutor* e) {
return;
}
f = std::move(f).via(e).then([](T&& t) { return std::move(t); });
while (!f.isReady()) {
e->drive();
auto now = std::chrono::steady_clock::now();
auto deadline = now + timeout;
while (!f.isReady() && (now < deadline)) {
e->try_drive_until(deadline);
now = std::chrono::steady_clock::now();
}
assert(f.isReady());
assert(f.isReady() || (now >= deadline));
}
} // namespace detail
......@@ -1458,18 +1464,6 @@ SemiFuture<T>&& SemiFuture<T>::wait(Duration dur) && {
return std::move(*this);
}
template <class T>
SemiFuture<T>& SemiFuture<T>::waitVia(DrivableExecutor* e) & {
futures::detail::waitViaImpl(*this, e);
return *this;
}
template <class T>
SemiFuture<T>&& SemiFuture<T>::waitVia(DrivableExecutor* e) && {
futures::detail::waitViaImpl(*this, e);
return std::move(*this);
}
template <class T>
T SemiFuture<T>::get() && {
return std::move(wait()).value();
......@@ -1500,16 +1494,6 @@ Try<T> SemiFuture<T>::getTry(Duration dur) && {
}
}
template <class T>
T SemiFuture<T>::getVia(DrivableExecutor* e) && {
return std::move(waitVia(e)).value();
}
template <class T>
Try<T> SemiFuture<T>::getTryVia(DrivableExecutor* e) && {
return std::move(waitVia(e)).result();
}
template <class T>
Future<T>& Future<T>::wait() & {
futures::detail::waitImpl(*this);
......@@ -1546,6 +1530,18 @@ Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
return std::move(*this);
}
template <class T>
Future<T>& Future<T>::waitVia(TimedDrivableExecutor* e, Duration dur) & {
futures::detail::waitViaImpl(*this, e, dur);
return *this;
}
template <class T>
Future<T>&& Future<T>::waitVia(TimedDrivableExecutor* e, Duration dur) && {
futures::detail::waitViaImpl(*this, e, dur);
return std::move(*this);
}
template <class T>
T Future<T>::get() {
return std::move(wait().value());
......@@ -1554,11 +1550,10 @@ T Future<T>::get() {
template <class T>
T Future<T>::get(Duration dur) {
wait(dur);
if (this->isReady()) {
return std::move(this->value());
} else {
if (!this->isReady()) {
throwTimedOut();
}
return std::move(this->value());
}
template <class T>
......@@ -1571,11 +1566,29 @@ T Future<T>::getVia(DrivableExecutor* e) {
return std::move(waitVia(e).value());
}
template <class T>
T Future<T>::getVia(TimedDrivableExecutor* e, Duration dur) {
waitVia(e, dur);
if (!this->isReady()) {
throwTimedOut();
}
return std::move(value());
}
template <class T>
Try<T>& Future<T>::getTryVia(DrivableExecutor* e) {
return waitVia(e).getTry();
}
template <class T>
Try<T>& Future<T>::getTryVia(TimedDrivableExecutor* e, Duration dur) {
waitVia(e, dur);
if (!this->isReady()) {
throwTimedOut();
}
return result();
}
namespace futures {
namespace detail {
template <class T>
......@@ -1595,7 +1608,7 @@ Future<bool> Future<T>::willEqual(Future<T>& f) {
std::get<0>(t), std::get<1>(t));
} else {
return false;
}
}
});
}
......
......@@ -28,6 +28,7 @@
#include <folly/Try.h>
#include <folly/Utility.h>
#include <folly/executors/DrivableExecutor.h>
#include <folly/executors/TimedDrivableExecutor.h>
#include <folly/futures/FutureException.h>
#include <folly/futures/Promise.h>
#include <folly/futures/detail/Types.h>
......@@ -200,6 +201,7 @@ class SemiFuture : private futures::detail::FutureBase<T> {
private:
using Base = futures::detail::FutureBase<T>;
using DeferredExecutor = futures::detail::DeferredExecutor;
using TimePoint = std::chrono::system_clock::time_point;
public:
static SemiFuture<T> makeEmpty(); // equivalent to moved-from
......@@ -242,9 +244,9 @@ class SemiFuture : private futures::detail::FutureBase<T> {
using Base::isReady;
using Base::poll;
using Base::raise;
using Base::result;
using Base::setCallback_;
using Base::value;
using Base::result;
SemiFuture& operator=(SemiFuture const&) = delete;
SemiFuture& operator=(SemiFuture&&) noexcept;
......@@ -267,16 +269,6 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// Try of the value (moved out) or may throw a TimedOut exception.
Try<T> getTry(Duration dur) &&;
/// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns the
/// value (moved out), or throws the exception.
T getVia(DrivableExecutor* e) &&;
/// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns the
/// Try of the value (moved out).
Try<T> getTryVia(DrivableExecutor* e) &&;
/// Block until this Future is complete. Returns a reference to this Future.
SemiFuture<T>& wait() &;
......@@ -290,15 +282,6 @@ class SemiFuture : private futures::detail::FutureBase<T> {
/// Overload of wait(Duration) for rvalue Futures
SemiFuture<T>&& wait(Duration) &&;
/// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns a
/// reference to this SemiFuture so that you can chain calls if desired.
/// value (moved out), or throws the exception.
SemiFuture<T>& waitVia(DrivableExecutor* e) &;
/// Overload of waitVia() for rvalue Futures
SemiFuture<T>&& waitVia(DrivableExecutor* e) &&;
/// Returns an inactive Future which will call back on the other side of
/// executor (when it is activated).
///
......@@ -445,11 +428,19 @@ class Future : private futures::detail::FutureBase<T> {
/// value (moved out), or throws the exception.
T getVia(DrivableExecutor* e);
/// getVia but will wait only until timed out. Returns the
/// Try of the value (moved out) or may throw a TimedOut exception.
T getVia(TimedDrivableExecutor* e, Duration dur);
/// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns a
/// reference to the Try of the value.
Try<T>& getTryVia(DrivableExecutor* e);
/// getTryVia but will wait only until timed out. Returns the
/// Try of the value (moved out) or may throw a TimedOut exception.
Try<T>& getTryVia(TimedDrivableExecutor* e, Duration dur);
/// Unwraps the case of a Future<Future<T>> instance, and returns a simple
/// Future<T> instance.
template <class F = T>
......@@ -701,6 +692,13 @@ class Future : private futures::detail::FutureBase<T> {
/// Overload of waitVia() for rvalue Futures
Future<T>&& waitVia(DrivableExecutor* e) &&;
/// As waitVia but may return early after dur passes.
Future<T>& waitVia(TimedDrivableExecutor* e, Duration dur) &;
/// Overload of waitVia() for rvalue Futures
/// As waitVia but may return early after dur passes.
Future<T>&& waitVia(TimedDrivableExecutor* e, Duration dur) &&;
/// If the value in this Future is equal to the given Future, when they have
/// both completed, the value of the resulting Future<bool> will be true. It
/// will be false otherwise (including when one or both Futures have an
......
......@@ -282,21 +282,34 @@ TEST(SemiFuture, SimpleGetTry) {
}
TEST(SemiFuture, SimpleTimedGet) {
EventBase e2;
Promise<folly::Unit> p;
auto sf = p.getSemiFuture();
EXPECT_THROW(std::move(sf).get(std::chrono::seconds(1)), TimedOut);
}
TEST(SemiFuture, SimpleTimedGetViaFromSemiFuture) {
TimedDrivableExecutor e2;
Promise<folly::Unit> p;
auto sf = p.getSemiFuture();
EXPECT_THROW(
std::move(sf).via(&e2).getVia(&e2, std::chrono::seconds(1)), TimedOut);
}
TEST(SemiFuture, SimpleTimedGetTry) {
EventBase e2;
Promise<folly::Unit> p;
auto sf = p.getSemiFuture();
EXPECT_THROW(std::move(sf).getTry(std::chrono::seconds(1)), TimedOut);
}
TEST(SemiFuture, SimpleTimedGetTryViaFromSemiFuture) {
TimedDrivableExecutor e2;
Promise<folly::Unit> p;
auto sf = p.getSemiFuture();
EXPECT_THROW(
std::move(sf).via(&e2).getTryVia(&e2, std::chrono::seconds(1)), TimedOut);
}
TEST(SemiFuture, SimpleValue) {
EventBase e2;
Promise<int> p;
auto sf = p.getSemiFuture();
p.setValue(3);
......@@ -305,7 +318,6 @@ TEST(SemiFuture, SimpleValue) {
}
TEST(SemiFuture, SimpleValueThrow) {
EventBase e2;
Promise<folly::Unit> p;
auto sf = p.getSemiFuture();
EXPECT_THROW(std::move(sf).value(), FutureNotReady);
......@@ -338,17 +350,6 @@ TEST(SemiFuture, SimpleDefer) {
ASSERT_EQ(innerResult, 17);
}
TEST(SemiFuture, DeferWithGetVia) {
std::atomic<int> innerResult{0};
EventBase e2;
Promise<folly::Unit> p;
auto f = p.getFuture();
auto sf = std::move(f).semi().defer([&]() { innerResult = 17; });
p.setValue();
std::move(sf).getVia(&e2);
ASSERT_EQ(innerResult, 17);
}
TEST(SemiFuture, DeferWithVia) {
std::atomic<int> innerResult{0};
EventBase e2;
......
......@@ -400,6 +400,13 @@ TEST(Via, getVia) {
}
}
TEST(Via, SimpleTimedGetVia) {
TimedDrivableExecutor e2;
Promise<folly::Unit> p;
auto f = p.getFuture();
EXPECT_THROW(f.getVia(&e2, std::chrono::seconds(1)), TimedOut);
}
TEST(Via, getTryVia) {
{
// non-void
......@@ -426,6 +433,13 @@ TEST(Via, getTryVia) {
}
}
TEST(Via, SimpleTimedGetTryVia) {
TimedDrivableExecutor e2;
Promise<folly::Unit> p;
auto f = p.getFuture();
EXPECT_THROW(f.getTryVia(&e2, std::chrono::seconds(1)), TimedOut);
}
TEST(Via, waitVia) {
{
ManualExecutor x;
......
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