folly::wangle::detail::getTimekeeperSingleton()->after(dur)
.then([&,token](Try<void> const& t) {
- try {
- t.value();
- if (token->exchange(true) == false) {
+ if (token->exchange(true) == false) {
+ try {
+ t.value();
p.setException(TimedOut());
- baton.post();
- }
- } catch (std::exception const& e) {
- if (token->exchange(true) == false) {
+ } catch (std::exception const& e) {
p.setException(std::current_exception());
- baton.post();
}
+ baton.post();
}
});
template <>
inline void Future<void>::get() {
getWaitHelper(this);
+ value();
}
template <class T>
}
template <class T>
-Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk)
-{
+Future<T> Future<T>::within(Duration dur, Timekeeper* tk) {
+ return within(dur, TimedOut(), tk);
+}
+
+template <class T>
+template <class E>
+Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
+
+ struct Context {
+ Context(E ex) : exception(std::move(ex)), promise(), token(false) {}
+ E exception;
+ Promise<T> promise;
+ std::atomic<bool> token;
+ };
+ auto ctx = std::make_shared<Context>(std::move(e));
+
+ if (!tk) {
+ tk = folly::wangle::detail::getTimekeeperSingleton();
+ }
+
+ tk->after(dur)
+ .then([ctx](Try<void> const& t) {
+ if (ctx->token.exchange(true) == false) {
+ try {
+ t.throwIfFailed();
+ ctx->promise.setException(std::move(ctx->exception));
+ } catch (std::exception const&) {
+ ctx->promise.setException(std::current_exception());
+ }
+ }
+ });
+
+ this->then([ctx](Try<T>&& t) {
+ if (ctx->token.exchange(true) == false) {
+ ctx->promise.fulfilTry(std::move(t));
+ }
+ });
+
+ return ctx->promise.getFuture();
+}
+
+template <class T>
+Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
return whenAll(*this, futures::sleep(dur, tk))
.then([](Try<std::tuple<Try<T>, Try<void>>>&& tup) {
Try<T>& t = std::get<0>(tup.value());
raise(FutureCancellation());
}
+ /// Throw TimedOut if this Future does not complete within the given
+ /// duration from now. The optional Timeekeeper is as with futures::sleep().
+ Future<T> within(Duration, Timekeeper* = nullptr);
+
+ /// Throw the given exception if this Future does not complete within the
+ /// given duration from now. The optional Timeekeeper is as with
+ /// futures::sleep().
+ template <class E>
+ Future<T> within(Duration, E exception, Timekeeper* = nullptr);
+
/// Delay the completion of this Future for at least this duration from
/// now. The optional Timekeeper is as with futures::sleep().
Future<T> delayed(Duration, Timekeeper* = nullptr);
EXPECT_GE(dur, one_ms);
}
+
+TEST(Timekeeper, futureWithinThrows) {
+ Promise<int> p;
+ auto f = p.getFuture()
+ .within(one_ms)
+ .onError([](TimedOut&) { return -1; });
+
+ EXPECT_EQ(-1, f.get());
+}
+
+TEST(Timekeeper, futureWithinAlreadyComplete) {
+ auto f = makeFuture(42)
+ .within(one_ms)
+ .onError([&](TimedOut&){ return -1; });
+
+ EXPECT_EQ(42, f.get());
+}
+
+TEST(Timekeeper, futureWithinFinishesInTime) {
+ Promise<int> p;
+ auto f = p.getFuture()
+ .within(std::chrono::minutes(1))
+ .onError([&](TimedOut&){ return -1; });
+ p.setValue(42);
+
+ EXPECT_EQ(42, f.get());
+}
+
+TEST(Timekeeper, futureWithinVoidSpecialization) {
+ makeFuture().within(one_ms);
+}
+
+TEST(Timekeeper, futureWithinException) {
+ Promise<void> p;
+ auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
+ EXPECT_THROW(f.get(), std::runtime_error);
+}