#include <chrono>
#include <thread>
-#include <folly/wangle/futures/detail/Core.h>
#include <folly/Baton.h>
+#include <folly/wangle/futures/detail/Core.h>
+#include <folly/wangle/futures/Timekeeper.h>
namespace folly { namespace wangle {
+class Timekeeper;
+
+namespace detail {
+ Timekeeper* getTimekeeperSingleton();
+}
+
template <typename T>
struct isFuture {
static const bool value = false;
setCallback_(
[p, funcm](Try<T>&& t) mutable {
p->fulfil([&]() {
- return (*funcm)(std::move(t));
- });
+ return (*funcm)(std::move(t));
+ });
});
return std::move(f);
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(std::move(t.exception()));
} else {
p->fulfil([&]() {
return (*funcm)(std::move(t.value()));
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(std::move(t.exception()));
} else {
p->fulfil([&]() {
return (*funcm)();
auto f2 = (*funcm)(std::move(t));
// that didn't throw, now we can steal p
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
+ p->fulfilTry(std::move(b));
+ });
+ } catch (const std::exception& e) {
+ p->setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
- p->setException(std::current_exception());
+ p->setException(exception_wrapper(std::current_exception()));
}
});
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(std::move(t.exception()));
} else {
try {
auto f2 = (*funcm)(std::move(t.value()));
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
+ p->fulfilTry(std::move(b));
+ });
+ } catch (const std::exception& e) {
+ p->setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
- p->setException(std::current_exception());
+ p->setException(exception_wrapper(std::current_exception()));
}
}
});
setCallback_(
[p, funcm](Try<T>&& t) mutable {
if (t.hasException()) {
- p->setException(t.getException());
+ p->setException(t.exception());
} else {
try {
auto f2 = (*funcm)();
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
+ p->fulfilTry(std::move(b));
+ });
+ } catch (const std::exception& e) {
+ p->setException(exception_wrapper(std::current_exception(), e));
} catch (...) {
- p->setException(std::current_exception());
+ p->setException(exception_wrapper(std::current_exception()));
}
}
});
auto pm = folly::makeMoveWrapper(std::move(p));
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T>&& t) mutable {
- try {
- t.throwIfFailed();
- } catch (Exn& e) {
- pm->fulfil([&]{
- return (*funcm)(e);
- });
- return;
- } catch (...) {
- // fall through
+ if (!t.template withException<Exn>([&] (Exn& e) {
+ pm->fulfil([&]{
+ return (*funcm)(e);
+ });
+ })) {
+ pm->fulfilTry(std::move(t));
}
- pm->fulfilTry(std::move(t));
});
return f;
auto pm = folly::makeMoveWrapper(std::move(p));
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T>&& t) mutable {
- try {
- t.throwIfFailed();
- } catch (Exn& e) {
- try {
- auto f2 = (*funcm)(e);
- f2.setCallback_([pm](Try<T>&& t2) mutable {
- pm->fulfilTry(std::move(t2));
- });
- } catch (...) {
- pm->setException(std::current_exception());
- }
- return;
- } catch (...) {
- // fall through
+ if (!t.template withException<Exn>([&] (Exn& e) {
+ try {
+ auto f2 = (*funcm)(e);
+ f2.setCallback_([pm](Try<T>&& t2) mutable {
+ pm->fulfilTry(std::move(t2));
+ });
+ } catch (const std::exception& e2) {
+ pm->setException(exception_wrapper(std::current_exception(), e2));
+ } catch (...) {
+ pm->setException(exception_wrapper(std::current_exception()));
+ }
+ })) {
+ pm->fulfilTry(std::move(t));
}
- pm->fulfilTry(std::move(t));
});
return f;
}
template <class T>
-void Future<T>::raise(std::exception_ptr exception) {
- core_->raise(exception);
+void Future<T>::raise(exception_wrapper exception) {
+ core_->raise(std::move(exception));
}
// makeFuture
return std::move(f);
}
+template <class T>
+Future<T> makeFuture(exception_wrapper ew) {
+ Promise<T> p;
+ p.setException(std::move(ew));
+ return p.getFuture();
+}
+
template <class T, class E>
typename std::enable_if<std::is_base_of<std::exception, E>::value,
Future<T>>::type
makeFuture(E const& e) {
Promise<T> p;
auto f = p.getFuture();
- p.fulfil([&]() -> T { throw e; });
+ p.setException(make_exception_wrapper<E>(e));
return std::move(f);
}
template <class T>
Future<T> makeFuture(Try<T>&& t) {
- try {
+ if (t.hasException()) {
+ return makeFuture<T>(std::move(t.exception()));
+ } else {
return makeFuture<T>(std::move(t.value()));
- } catch (...) {
- return makeFuture<T>(std::current_exception());
}
}
template <>
inline Future<void> makeFuture(Try<void>&& t) {
- try {
- t.throwIfFailed();
+ if (t.hasException()) {
+ return makeFuture<void>(std::move(t.exception()));
+ } else {
return makeFuture();
- } catch (...) {
- return makeFuture<void>(std::current_exception());
}
}
return done;
}
-template <typename T, class Duration>
+template <typename T, class Dur>
Future<T>
-waitWithSemaphore(Future<T>&& f, Duration timeout) {
+waitWithSemaphore(Future<T>&& f, Dur timeout) {
auto baton = std::make_shared<Baton<>>();
auto done = f.then([baton](Try<T> &&t) {
baton->post();
return done;
}
-template <class Duration>
+template <class Dur>
Future<void>
-waitWithSemaphore(Future<void>&& f, Duration timeout) {
+waitWithSemaphore(Future<void>&& f, Dur timeout) {
auto baton = std::make_shared<Baton<>>();
auto done = f.then([baton](Try<void> &&t) {
baton->post();
return done;
}
+namespace {
+ template <class T>
+ void getWaitHelper(Future<T>* f) {
+ // If we already have a value do the cheap thing
+ if (f->isReady()) {
+ return;
+ }
+
+ folly::Baton<> baton;
+ f->then([&](Try<T> const&) {
+ baton.post();
+ });
+ baton.wait();
+ }
+
+ template <class T>
+ Future<T> getWaitTimeoutHelper(Future<T>* f, Duration dur) {
+ // TODO make and use variadic whenAny #5877971
+ Promise<T> p;
+ auto token = std::make_shared<std::atomic<bool>>();
+ folly::Baton<> baton;
+
+ folly::wangle::detail::getTimekeeperSingleton()->after(dur)
+ .then([&,token](Try<void> const& t) {
+ if (token->exchange(true) == false) {
+ try {
+ t.value();
+ p.setException(TimedOut());
+ } catch (std::exception const& e) {
+ p.setException(exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ p.setException(exception_wrapper(std::current_exception()));
+ }
+ baton.post();
+ }
+ });
+
+ f->then([&, token](Try<T>&& t) {
+ if (token->exchange(true) == false) {
+ p.fulfilTry(std::move(t));
+ baton.post();
+ }
+ });
+
+ baton.wait();
+ return p.getFuture();
+ }
+}
+
+template <class T>
+T Future<T>::get() {
+ getWaitHelper(this);
+
+ // Big assumption here: the then() call above, since it doesn't move out
+ // the value, leaves us with a value to return here. This would be a big
+ // no-no in user code, but I'm invoking internal developer privilege. This
+ // is slightly more efficient (save a move()) especially if there's an
+ // exception (save a throw).
+ return std::move(value());
+}
+
+template <>
+inline void Future<void>::get() {
+ getWaitHelper(this);
+ value();
+}
+
+template <class T>
+T Future<T>::get(Duration dur) {
+ return std::move(getWaitTimeoutHelper(this, dur).value());
+}
+
+template <>
+inline void Future<void>::get(Duration dur) {
+ getWaitTimeoutHelper(this, dur).value();
+}
+
+template <class T>
+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& e2) {
+ ctx->promise.setException(
+ exception_wrapper(std::current_exception(), e2));
+ } catch (...) {
+ ctx->promise.setException(
+ exception_wrapper(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());
+ return makeFuture<T>(std::move(t));
+ });
+}
+
}}
// I haven't included a Future<T&> specialization because I don't forsee us