/*
- * Copyright 2014 Facebook, Inc.
+ * 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.
#include <thread>
#include <folly/Baton.h>
+#include <folly/Optional.h>
#include <folly/futures/detail/Core.h>
#include <folly/futures/Timekeeper.h>
Timekeeper* getTimekeeperSingleton();
}
-template <typename T>
-struct isFuture {
- static const bool value = false;
-};
-
-template <typename T>
-struct isFuture<Future<T> > {
- static const bool value = true;
-};
-
template <class T>
-Future<T>::Future(Future<T>&& other) noexcept : core_(nullptr) {
- *this = std::move(other);
+Future<T>::Future(Future<T>&& other) noexcept : core_(other.core_) {
+ other.core_ = nullptr;
}
template <class T>
-Future<T>& Future<T>::operator=(Future<T>&& other) {
+Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
std::swap(core_, other.core_);
return *this;
}
+template <class T>
+template <class F>
+Future<T>::Future(
+ const typename std::enable_if<!std::is_void<F>::value, F>::type& val)
+ : core_(nullptr) {
+ Promise<F> p;
+ p.setValue(val);
+ *this = p.getFuture();
+}
+
+template <class T>
+template <class F>
+Future<T>::Future(
+ typename std::enable_if<!std::is_void<F>::value, F>::type&& val)
+ : core_(nullptr) {
+ Promise<F> p;
+ p.setValue(std::forward<F>(val));
+ *this = p.getFuture();
+}
+
+template <>
+template <class F,
+ typename std::enable_if<std::is_void<F>::value, int>::type>
+Future<void>::Future() : core_(nullptr) {
+ Promise<void> p;
+ p.setValue();
+ *this = p.getFuture();
+}
+
+
template <class T>
Future<T>::~Future() {
detach();
core_->setCallback(std::move(func));
}
-// Variant: f.then([](Try<T>&& t){ return t.value(); });
+// unwrap
+
template <class T>
template <class F>
-typename std::enable_if<
- !isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
- Future<typename std::result_of<F(Try<T>&&)>::type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(Try<T>&&)>::type B;
+typename std::enable_if<isFuture<F>::value,
+ Future<typename isFuture<T>::Inner>>::type
+Future<T>::unwrap() {
+ return then([](Future<typename isFuture<T>::Inner> internal_future) {
+ return internal_future;
+ });
+}
+
+// then
+
+// Variant: returns a value
+// e.g. f.then([](Try<T>&& t){ return t.value(); });
+template <class T>
+template <typename F, typename R, bool isTry, typename... Args>
+typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
+Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
+ static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
+ typedef typename R::ReturnsFuture::Inner B;
throwIfInvalid();
// grab the Future now before we lose our handle on the Promise
auto f = p->getFuture();
+ if (getExecutor()) {
+ f.setExecutor(getExecutor());
+ }
/* This is a bit tricky.
*/
setCallback_(
[p, funcm](Try<T>&& t) mutable {
- p->fulfil([&]() {
- return (*funcm)(std::move(t));
- });
- });
-
- return f;
-}
-
-// Variant: f.then([](T&& t){ return t; });
-template <class T>
-template <class F>
-typename std::enable_if<
- !std::is_same<T, void>::value &&
- !isFuture<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
- Future<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(T&&)>::type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
- p->setException(std::move(t.exception()));
- } else {
- p->fulfil([&]() {
- return (*funcm)(std::move(t.value()));
- });
- }
- });
-
- return f;
-}
-
-// Variant: f.then([](){ return; });
-template <class T>
-template <class F>
-typename std::enable_if<
- std::is_same<T, void>::value &&
- !isFuture<typename std::result_of<F()>::type>::value,
- Future<typename std::result_of<F()>::type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F()>::type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
+ if (!isTry && t.hasException()) {
p->setException(std::move(t.exception()));
} else {
- p->fulfil([&]() {
- return (*funcm)();
+ p->setWith([&]() {
+ return (*funcm)(t.template get<isTry, Args>()...);
});
}
});
return f;
}
-// Variant: f.then([](Try<T>&& t){ return makeFuture<T>(t.value()); });
+// Variant: returns a Future
+// e.g. f.then([](T&& t){ return makeFuture<T>(t); });
template <class T>
-template <class F>
-typename std::enable_if<
- isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
- Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(Try<T>&&)>::type::value_type B;
+template <typename F, typename R, bool isTry, typename... Args>
+typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
+Future<T>::thenImplementation(F func, detail::argResult<isTry, F, Args...>) {
+ static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
+ typedef typename R::ReturnsFuture::Inner B;
throwIfInvalid();
// grab the Future now before we lose our handle on the Promise
auto f = p->getFuture();
+ if (getExecutor()) {
+ f.setExecutor(getExecutor());
+ }
setCallback_(
[p, funcm](Try<T>&& t) mutable {
- try {
- 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));
- });
- } catch (const std::exception& e) {
- p->setException(exception_wrapper(std::current_exception(), e));
- } catch (...) {
- p->setException(exception_wrapper(std::current_exception()));
- }
- });
-
- return f;
-}
-
-// Variant: f.then([](T&& t){ return makeFuture<T>(t); });
-template <class T>
-template <class F>
-typename std::enable_if<
- !std::is_same<T, void>::value &&
- isFuture<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type>::value,
- Future<typename std::result_of<
- F(typename detail::AliasIfVoid<T>::type&&)>::type::value_type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F(T&&)>::type::value_type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
+ if (!isTry && t.hasException()) {
p->setException(std::move(t.exception()));
} else {
try {
- auto f2 = (*funcm)(std::move(t.value()));
+ auto f2 = (*funcm)(t.template get<isTry, Args>()...);
+ // that didn't throw, now we can steal p
f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
+ p->setTry(std::move(b));
});
} catch (const std::exception& e) {
p->setException(exception_wrapper(std::current_exception(), e));
return f;
}
-// Variant: f.then([](){ return makeFuture(); });
-template <class T>
-template <class F>
-typename std::enable_if<
- std::is_same<T, void>::value &&
- isFuture<typename std::result_of<F()>::type>::value,
- Future<typename std::result_of<F()>::type::value_type> >::type
-Future<T>::then(F&& func) {
- typedef typename std::result_of<F()>::type::value_type B;
-
- throwIfInvalid();
-
- folly::MoveWrapper<Promise<B>> p;
- folly::MoveWrapper<F> funcm(std::forward<F>(func));
-
- auto f = p->getFuture();
-
- setCallback_(
- [p, funcm](Try<T>&& t) mutable {
- if (t.hasException()) {
- p->setException(t.exception());
- } else {
- try {
- auto f2 = (*funcm)();
- f2.setCallback_([p](Try<B>&& b) mutable {
- p->fulfilTry(std::move(b));
- });
- } catch (const std::exception& e) {
- p->setException(exception_wrapper(std::current_exception(), e));
- } catch (...) {
- p->setException(exception_wrapper(std::current_exception()));
- }
- }
- });
+template <typename T>
+template <typename R, typename Caller, typename... Args>
+ Future<typename isFuture<R>::Inner>
+Future<T>::then(R(Caller::*func)(Args...), Caller *instance) {
+ typedef typename std::remove_cv<
+ typename std::remove_reference<
+ typename detail::ArgType<Args...>::FirstArg>::type>::type FirstArg;
+ return then([instance, func](Try<T>&& t){
+ return (instance->*func)(t.template get<isTry<FirstArg>::value, Args>()...);
+ });
+}
- return f;
+// TODO(6838553)
+#ifndef __clang__
+template <class T>
+template <class... Args>
+auto Future<T>::then(Executor* x, Args&&... args)
+ -> decltype(this->then(std::forward<Args>(args)...))
+{
+ auto oldX = getExecutor();
+ setExecutor(x);
+ return this->then(std::forward<Args>(args)...).via(oldX);
}
+#endif
template <class T>
Future<void> Future<T>::then() {
template <class T>
template <class F>
typename std::enable_if<
+ !detail::callableWith<F, exception_wrapper>::value &&
!detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) {
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T>&& t) mutable {
if (!t.template withException<Exn>([&] (Exn& e) {
- pm->fulfil([&]{
+ pm->setWith([&]{
return (*funcm)(e);
});
})) {
- pm->fulfilTry(std::move(t));
+ pm->setTry(std::move(t));
}
});
template <class T>
template <class F>
typename std::enable_if<
+ !detail::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) {
try {
auto f2 = (*funcm)(e);
f2.setCallback_([pm](Try<T>&& t2) mutable {
- pm->fulfilTry(std::move(t2));
+ pm->setTry(std::move(t2));
});
} catch (const std::exception& e2) {
pm->setException(exception_wrapper(std::current_exception(), e2));
pm->setException(exception_wrapper(std::current_exception()));
}
})) {
- pm->fulfilTry(std::move(t));
+ pm->setTry(std::move(t));
+ }
+ });
+
+ return f;
+}
+
+template <class T>
+template <class F>
+Future<T> Future<T>::ensure(F func) {
+ MoveWrapper<F> funcw(std::move(func));
+ return this->then([funcw](Try<T>&& t) {
+ (*funcw)();
+ return makeFuture(std::move(t));
+ });
+}
+
+template <class T>
+template <class F>
+Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) {
+ auto funcw = folly::makeMoveWrapper(std::forward<F>(func));
+ return within(dur, tk)
+ .onError([funcw](TimedOut const&) { return (*funcw)(); });
+}
+
+template <class T>
+template <class F>
+typename std::enable_if<
+ detail::callableWith<F, exception_wrapper>::value &&
+ detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+Future<T>::onError(F&& func) {
+ static_assert(
+ std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
+ "Return type of onError callback must be T or Future<T>");
+
+ Promise<T> p;
+ auto f = p.getFuture();
+ auto pm = folly::makeMoveWrapper(std::move(p));
+ auto funcm = folly::makeMoveWrapper(std::move(func));
+ setCallback_([pm, funcm](Try<T> t) mutable {
+ if (t.hasException()) {
+ try {
+ auto f2 = (*funcm)(std::move(t.exception()));
+ f2.setCallback_([pm](Try<T> t2) mutable {
+ pm->setTry(std::move(t2));
+ });
+ } catch (const std::exception& e2) {
+ pm->setException(exception_wrapper(std::current_exception(), e2));
+ } catch (...) {
+ pm->setException(exception_wrapper(std::current_exception()));
+ }
+ } else {
+ pm->setTry(std::move(t));
+ }
+ });
+
+ return f;
+}
+
+// onError(exception_wrapper) that returns T
+template <class T>
+template <class F>
+typename std::enable_if<
+ detail::callableWith<F, exception_wrapper>::value &&
+ !detail::Extract<F>::ReturnsFuture::value,
+ Future<T>>::type
+Future<T>::onError(F&& func) {
+ static_assert(
+ std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
+ "Return type of onError callback must be T or Future<T>");
+
+ Promise<T> p;
+ auto f = p.getFuture();
+ auto pm = folly::makeMoveWrapper(std::move(p));
+ auto funcm = folly::makeMoveWrapper(std::move(func));
+ setCallback_([pm, funcm](Try<T> t) mutable {
+ if (t.hasException()) {
+ pm->setWith([&]{
+ return (*funcm)(std::move(t.exception()));
+ });
+ } else {
+ pm->setTry(std::move(t));
}
});
return core_->getTry();
}
+template <class T>
+Optional<Try<T>> Future<T>::poll() {
+ Optional<Try<T>> o;
+ if (core_->ready()) {
+ o = std::move(core_->getTry());
+ }
+ return o;
+}
+
template <class T>
template <typename Executor>
inline Future<T> Future<T>::via(Executor* executor) && {
throwIfInvalid();
- this->deactivate();
- core_->setExecutor(executor);
+ setExecutor(executor);
return std::move(*this);
}
MoveWrapper<Promise<T>> p;
auto f = p->getFuture();
- then([p](Try<T>&& t) mutable { p->fulfilTry(std::move(t)); });
+ then([p](Try<T>&& t) mutable { p->setTry(std::move(t)); });
return std::move(f).via(executor);
}
typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
-> Future<decltype(func())> {
Promise<decltype(func())> p;
- p.fulfil(
+ p.setWith(
[&func]() {
return (func)();
});
template <class T>
Future<T> makeFuture(Try<T>&& t) {
- if (t.hasException()) {
- return makeFuture<T>(std::move(t.exception()));
- } else {
- return makeFuture<T>(std::move(t.value()));
- }
+ Promise<typename std::decay<T>::type> p;
+ p.setTry(std::move(t));
+ return p.getFuture();
}
template <>
template <typename... Fs>
typename detail::VariadicContext<
typename std::decay<Fs>::type::value_type...>::type
-whenAll(Fs&&... fs)
-{
+whenAll(Fs&&... fs) {
auto ctx =
new detail::VariadicContext<typename std::decay<Fs>::type::value_type...>();
ctx->total = sizeof...(fs);
Future<
std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
-whenAll(InputIterator first, InputIterator last)
-{
+whenAll(InputIterator first, InputIterator last) {
typedef
typename std::iterator_traits<InputIterator>::value_type::value_type T;
for (size_t i = 0; first != last; ++first, ++i) {
assert(i < n);
auto& f = *first;
- f.setCallback_([ctx, i, n](Try<T>&& t) {
- ctx->results[i] = std::move(t);
- if (++ctx->count == n) {
- ctx->p.setValue(std::move(ctx->results));
- delete ctx;
+ f.setCallback_([ctx, i, n](Try<T> t) {
+ ctx->results[i] = std::move(t);
+ if (++ctx->count == n) {
+ ctx->p.setValue(std::move(ctx->results));
+ delete ctx;
+ }
+ });
+ }
+
+ return f_saved;
+}
+
+namespace detail {
+
+template <class, class, typename = void> struct CollectContextHelper;
+
+template <class T, class VecT>
+struct CollectContextHelper<T, VecT,
+ typename std::enable_if<std::is_same<T, VecT>::value>::type> {
+ static inline std::vector<T>& getResults(std::vector<VecT>& results) {
+ return results;
+ }
+};
+
+template <class T, class VecT>
+struct CollectContextHelper<T, VecT,
+ typename std::enable_if<!std::is_same<T, VecT>::value>::type> {
+ static inline std::vector<T> getResults(std::vector<VecT>& results) {
+ std::vector<T> finalResults;
+ finalResults.reserve(results.size());
+ for (auto& opt : results) {
+ finalResults.push_back(std::move(opt.value()));
+ }
+ return finalResults;
+ }
+};
+
+template <typename T>
+struct CollectContext {
+
+ typedef typename std::conditional<
+ std::is_default_constructible<T>::value,
+ T,
+ Optional<T>
+ >::type VecT;
+
+ explicit CollectContext(int n) : count(0), threw(false) {
+ results.resize(n);
+ }
+
+ Promise<std::vector<T>> p;
+ std::vector<VecT> results;
+ std::atomic<size_t> count;
+ std::atomic_bool threw;
+
+ typedef std::vector<T> result_type;
+
+ static inline Future<std::vector<T>> makeEmptyFuture() {
+ return makeFuture(std::vector<T>());
+ }
+
+ inline void setValue() {
+ p.setValue(CollectContextHelper<T, VecT>::getResults(results));
+ }
+
+ inline void addResult(int i, Try<T>& t) {
+ results[i] = std::move(t.value());
+ }
+};
+
+template <>
+struct CollectContext<void> {
+
+ explicit CollectContext(int n) : count(0), threw(false) {}
+
+ Promise<void> p;
+ std::atomic<size_t> count;
+ std::atomic_bool threw;
+
+ typedef void result_type;
+
+ static inline Future<void> makeEmptyFuture() {
+ return makeFuture();
+ }
+
+ inline void setValue() {
+ p.setValue();
+ }
+
+ inline void addResult(int i, Try<void>& t) {
+ // do nothing
+ }
+};
+
+} // detail
+
+template <class InputIterator>
+Future<typename detail::CollectContext<
+ typename std::iterator_traits<InputIterator>::value_type::value_type
+>::result_type>
+collect(InputIterator first, InputIterator last) {
+ typedef
+ typename std::iterator_traits<InputIterator>::value_type::value_type T;
+
+ if (first >= last) {
+ return detail::CollectContext<T>::makeEmptyFuture();
+ }
+
+ size_t n = std::distance(first, last);
+ auto ctx = new detail::CollectContext<T>(n);
+ auto f_saved = ctx->p.getFuture();
+
+ for (size_t i = 0; first != last; ++first, ++i) {
+ assert(i < n);
+ auto& f = *first;
+ f.setCallback_([ctx, i, n](Try<T> t) {
+ auto c = ++ctx->count;
+
+ if (t.hasException()) {
+ if (!ctx->threw.exchange(true)) {
+ ctx->p.setException(std::move(t.exception()));
}
- });
+ } else if (!ctx->threw) {
+ ctx->addResult(i, t);
+ if (c == n) {
+ ctx->setValue();
+ }
+ }
+
+ if (c == n) {
+ delete ctx;
+ }
+ });
}
return f_saved;
ctx->completed = 0;
// for each completed Future, increase count and add to vector, until we
- // have n completed futures at which point we fulfil our Promise with the
+ // have n completed futures at which point we fulfill our Promise with the
// vector
auto it = first;
size_t i = 0;
assert(ctx->v.size() < n);
v.push_back(std::make_pair(i, std::move(t)));
if (c == n) {
- ctx->p.fulfilTry(Try<V>(std::move(v)));
+ ctx->p.setTry(Try<V>(std::move(v)));
}
}
});
return ctx->p.getFuture();
}
-template <typename T>
-Future<T>
-waitWithSemaphore(Future<T>&& f) {
- Baton<> baton;
- auto done = f.then([&](Try<T> &&t) {
- baton.post();
- return std::move(t.value());
- });
- baton.wait();
- while (!done.isReady()) {
- // There's a race here between the return here and the actual finishing of
- // the future. f is completed, but the setup may not have finished on done
- // after the baton has posted.
- std::this_thread::yield();
- }
- return done;
-}
-
-template<>
-inline Future<void> waitWithSemaphore<void>(Future<void>&& f) {
- Baton<> baton;
- auto done = f.then([&](Try<void> &&t) {
- baton.post();
- t.value();
- });
- baton.wait();
- while (!done.isReady()) {
- // There's a race here between the return here and the actual finishing of
- // the future. f is completed, but the setup may not have finished on done
- // after the baton has posted.
- std::this_thread::yield();
+template <class It, class T, class F, class ItT, class Arg>
+typename std::enable_if<!isFutureResult<F, T, Arg>::value, Future<T>>::type
+reduce(It first, It last, T initial, F func) {
+ if (first == last) {
+ return makeFuture(std::move(initial));
}
- return done;
-}
-template <typename T, class Dur>
-Future<T>
-waitWithSemaphore(Future<T>&& f, Dur timeout) {
- auto baton = std::make_shared<Baton<>>();
- auto done = f.then([baton](Try<T> &&t) {
- baton->post();
- return std::move(t.value());
- });
- baton->timed_wait(std::chrono::system_clock::now() + timeout);
- return done;
-}
+ typedef isTry<Arg> IsTry;
-template <class Dur>
-Future<void>
-waitWithSemaphore(Future<void>&& f, Dur timeout) {
- auto baton = std::make_shared<Baton<>>();
- auto done = f.then([baton](Try<void> &&t) {
- baton->post();
- t.value();
- });
- baton->timed_wait(std::chrono::system_clock::now() + timeout);
- return done;
+ return whenAll(first, last)
+ .then([initial, func](std::vector<Try<ItT>>& vals) mutable {
+ for (auto& val : vals) {
+ initial = func(std::move(initial),
+ // Either return a ItT&& or a Try<ItT>&& depending
+ // on the type of the argument of func.
+ val.template get<IsTry::value, Arg&&>());
+ }
+ return initial;
+ });
}
-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 It, class T, class F, class ItT, class Arg>
+typename std::enable_if<isFutureResult<F, T, Arg>::value, Future<T>>::type
+reduce(It first, It last, T initial, F func) {
+ if (first == last) {
+ return makeFuture(std::move(initial));
}
- 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;
+ typedef isTry<Arg> IsTry;
- folly::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();
- }
- });
+ auto f = first->then([initial, func](Try<ItT>& head) mutable {
+ return func(std::move(initial),
+ head.template get<IsTry::value, Arg&&>());
+ });
- f->then([&, token](Try<T>&& t) {
- if (token->exchange(true) == false) {
- p.fulfilTry(std::move(t));
- baton.post();
- }
+ for (++first; first != last; ++first) {
+ f = whenAll(f, *first).then([func](std::tuple<Try<T>, Try<ItT>>& t) {
+ return func(std::move(std::get<0>(t).value()),
+ // Either return a ItT&& or a Try<ItT>&& depending
+ // on the type of the argument of func.
+ std::get<1>(t).template get<IsTry::value, Arg&&>());
});
-
- 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();
+ return f;
}
template <class T>
tk->after(dur)
.then([ctx](Try<void> const& t) {
if (ctx->token.exchange(true) == false) {
- try {
- t.throwIfFailed();
+ if (t.hasException()) {
+ ctx->promise.setException(std::move(t.exception()));
+ } else {
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));
+ ctx->promise.setTry(std::move(t));
}
});
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());
+ .then([](std::tuple<Try<T>, Try<void>> tup) {
+ Try<T>& t = std::get<0>(tup);
return makeFuture<T>(std::move(t));
});
}
+namespace detail {
+
+template <class T>
+void waitImpl(Future<T>& f) {
+ // short-circuit if there's nothing to do
+ if (f.isReady()) return;
+
+ Baton<> baton;
+ f = f.then([&](Try<T> t) {
+ baton.post();
+ return makeFuture(std::move(t));
+ });
+ baton.wait();
+
+ // There's a race here between the return here and the actual finishing of
+ // the future. f is completed, but the setup may not have finished on done
+ // after the baton has posted.
+ while (!f.isReady()) {
+ std::this_thread::yield();
+ }
+}
+
+template <class T>
+void waitImpl(Future<T>& f, Duration dur) {
+ // short-circuit if there's nothing to do
+ if (f.isReady()) return;
+
+ auto baton = std::make_shared<Baton<>>();
+ f = f.then([baton](Try<T> t) {
+ baton->post();
+ return makeFuture(std::move(t));
+ });
+
+ // Let's preserve the invariant that if we did not timeout (timed_wait returns
+ // true), then the returned Future is complete when it is returned to the
+ // caller. We need to wait out the race for that Future to complete.
+ if (baton->timed_wait(std::chrono::system_clock::now() + dur)) {
+ while (!f.isReady()) {
+ std::this_thread::yield();
+ }
+ }
+}
+
+template <class T>
+void waitViaImpl(Future<T>& f, DrivableExecutor* e) {
+ while (!f.isReady()) {
+ e->drive();
+ }
+}
+
+} // detail
+
+template <class T>
+Future<T>& Future<T>::wait() & {
+ detail::waitImpl(*this);
+ return *this;
+}
+
+template <class T>
+Future<T>&& Future<T>::wait() && {
+ detail::waitImpl(*this);
+ return std::move(*this);
+}
+
+template <class T>
+Future<T>& Future<T>::wait(Duration dur) & {
+ detail::waitImpl(*this, dur);
+ return *this;
+}
+
+template <class T>
+Future<T>&& Future<T>::wait(Duration dur) && {
+ detail::waitImpl(*this, dur);
+ return std::move(*this);
+}
+
+template <class T>
+Future<T>& Future<T>::waitVia(DrivableExecutor* e) & {
+ detail::waitViaImpl(*this, e);
+ return *this;
+}
+
+template <class T>
+Future<T>&& Future<T>::waitVia(DrivableExecutor* e) && {
+ detail::waitViaImpl(*this, e);
+ return std::move(*this);
}
+template <class T>
+T Future<T>::get() {
+ return std::move(wait().value());
+}
+
+template <>
+inline void Future<void>::get() {
+ wait().value();
+}
+
+template <class T>
+T Future<T>::get(Duration dur) {
+ wait(dur);
+ if (isReady()) {
+ return std::move(value());
+ } else {
+ throw TimedOut();
+ }
+}
+
+template <>
+inline void Future<void>::get(Duration dur) {
+ wait(dur);
+ if (isReady()) {
+ return;
+ } else {
+ throw TimedOut();
+ }
+}
+
+template <class T>
+T Future<T>::getVia(DrivableExecutor* e) {
+ return std::move(waitVia(e).value());
+}
+
+template <>
+inline void Future<void>::getVia(DrivableExecutor* e) {
+ waitVia(e).value();
+}
+
+template <class T>
+Future<bool> Future<T>::willEqual(Future<T>& f) {
+ return whenAll(*this, f).then([](const std::tuple<Try<T>, Try<T>>& t) {
+ if (std::get<0>(t).hasValue() && std::get<1>(t).hasValue()) {
+ return std::get<0>(t).value() == std::get<1>(t).value();
+ } else {
+ return false;
+ }
+ });
+}
+
+template <class T>
+template <class F>
+Future<T> Future<T>::filter(F predicate) {
+ auto p = folly::makeMoveWrapper(std::move(predicate));
+ return this->then([p](T val) {
+ T const& valConstRef = val;
+ if (!(*p)(valConstRef)) {
+ throw PredicateDoesNotObtain();
+ }
+ return val;
+ });
+}
+
+namespace futures {
+ namespace {
+ template <class Z>
+ Future<Z> chainHelper(Future<Z> f) {
+ return f;
+ }
+
+ template <class Z, class F, class Fn, class... Callbacks>
+ Future<Z> chainHelper(F f, Fn fn, Callbacks... fns) {
+ return chainHelper<Z>(f.then(fn), fns...);
+ }
+ }
+
+ template <class A, class Z, class... Callbacks>
+ std::function<Future<Z>(Try<A>)>
+ chain(Callbacks... fns) {
+ MoveWrapper<Promise<A>> pw;
+ MoveWrapper<Future<Z>> fw(chainHelper<Z>(pw->getFuture(), fns...));
+ return [=](Try<A> t) mutable {
+ pw->setTry(std::move(t));
+ return std::move(*fw);
+ };
+ }
+
+ template <class It, class F, class ItT, class Result>
+ std::vector<Future<Result>> map(It first, It last, F func) {
+ std::vector<Future<Result>> results;
+ for (auto it = first; it != last; it++) {
+ results.push_back(it->then(func));
+ }
+ return results;
+ }
+}
+
+} // namespace folly
+
// I haven't included a Future<T&> specialization because I don't forsee us
// using it, however it is not difficult to add when needed. Refer to
// Future<void> for guidance. std::future and boost::future code would also be