Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
struct Context {
- Context(E ex, Future<Unit>&& f)
- : exception(std::move(ex)), afterFuture(std::move(f)), promise() {}
+ Context(E ex) : exception(std::move(ex)), promise() {}
E exception;
- Future<Unit> afterFuture;
- Future<Unit> thisFuture;
Promise<T> promise;
std::atomic<bool> token {false};
};
+ auto ctx = std::make_shared<Context>(std::move(e));
if (!tk) {
tk = folly::detail::getTimekeeperSingleton();
}
- auto ctx = std::make_shared<Context>(std::move(e), tk->after(dur));
+ tk->after(dur)
+ .then([ctx](Try<Unit> const& t) {
+ if (ctx->token.exchange(true) == false) {
+ if (t.hasException()) {
+ ctx->promise.setException(std::move(t.exception()));
+ } else {
+ ctx->promise.setException(std::move(ctx->exception));
+ }
+ }
+ });
- ctx->thisFuture = this->then([ctx](Try<T>&& t) mutable {
- // "this" completed first, cancel "after"
- ctx->afterFuture.raise(CancelTimer());
+ this->then([ctx](Try<T>&& t) {
if (ctx->token.exchange(true) == false) {
ctx->promise.setTry(std::move(t));
}
});
- ctx->afterFuture.then([ctx](Try<Unit> const& t) mutable {
- // "after" completed first, cancel "this"
- ctx->thisFuture.raise(TimedOut());
- if (ctx->token.exchange(true) == false) {
- if (t.hasException()) {
- ctx->promise.setException(std::move(t.exception()));
- } else {
- ctx->promise.setException(std::move(ctx->exception));
- }
- }
- });
-
return ctx->promise.getFuture().via(getExecutor());
}
#include <folly/futures/Future.h>
#include <folly/futures/Promise.h>
-#include <folly/Baton.h>
using namespace folly;
f.cancel();
EXPECT_EQ(1, count);
}
-
-TEST(Interrupt, withinTimedOut) {
- Promise<int> p;
- Baton<> done;
- p.setInterruptHandler([&](const exception_wrapper& e) { done.post(); });
- p.getFuture().within(std::chrono::milliseconds(1));
- // Give it 100ms to time out and call the interrupt handler
- auto t = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
- EXPECT_TRUE(done.timed_wait(t));
-}
-
-class DummyTimeKeeper : public Timekeeper {
- public:
- explicit DummyTimeKeeper() : interrupted() {}
-
- Future<Unit> after(Duration) override {
- promise.setInterruptHandler(
- [this](const exception_wrapper& e) {
- EXPECT_THROW(e.throwException(), CancelTimer);
- interrupted.post();
- }
- );
- return promise.getFuture();
- }
-
- Baton<> interrupted;
-
- private:
- Promise<Unit> promise;
-};
-
-TEST(Interrupt, withinCancelTimer) {
- DummyTimeKeeper tk;
- Promise<int> p;
- Baton<> done;
- p.getFuture().within(std::chrono::milliseconds(10), TimedOut(), &tk);
- p.setValue(1); // this should cancel the timer
- // Give it 100ms to interrupt the timer Future
- auto t = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
- EXPECT_TRUE(tk.interrupted.timed_wait(t));
-}