then-with-Executor
[folly.git] / folly / futures / Future-inl.h
index b45545f9b86cab6c726a18e0670f1a2209dc132b..fb110d096e41a5324d542e0ae2a2fb2e7a46b4f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -20,6 +20,7 @@
 #include <thread>
 
 #include <folly/Baton.h>
+#include <folly/Optional.h>
 #include <folly/futures/detail/Core.h>
 #include <folly/futures/Timekeeper.h>
 
@@ -31,27 +32,47 @@ namespace detail {
   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();
@@ -78,14 +99,28 @@ void Future<T>::setCallback_(F&& func) {
   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();
 
@@ -95,6 +130,9 @@ Future<T>::then(F&& func) {
 
   // 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.
 
@@ -130,69 +168,11 @@ Future<T>::then(F&& func) {
      */
   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>()...);
         });
       }
     });
@@ -200,14 +180,14 @@ Future<T>::then(F&& func) {
   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();
 
@@ -217,52 +197,20 @@ Future<T>::then(F&& func) {
 
   // 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));
@@ -275,43 +223,30 @@ Future<T>::then(F&& func) {
   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() {
@@ -322,6 +257,7 @@ 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) {
@@ -336,11 +272,11 @@ 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));
     }
   });
 
@@ -351,6 +287,7 @@ Future<T>::onError(F&& func) {
 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) {
@@ -368,7 +305,7 @@ 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));
@@ -376,7 +313,89 @@ Future<T>::onError(F&& func) {
             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));
     }
   });
 
@@ -404,13 +423,21 @@ Try<T>& Future<T>::getTry() {
   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);
 }
@@ -422,7 +449,7 @@ inline Future<T> Future<T>::via(Executor* executor) & {
 
   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);
 }
 
@@ -459,7 +486,7 @@ auto makeFutureTry(
     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)();
     });
@@ -497,11 +524,9 @@ makeFuture(E const& e) {
 
 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 <>
@@ -524,8 +549,7 @@ Future<void> via(Executor* executor) {
 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);
@@ -541,8 +565,7 @@ template <class InputIterator>
 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;
 
@@ -560,13 +583,139 @@ whenAll(InputIterator first, InputIterator last)
   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;
@@ -615,7 +764,7 @@ whenN(InputIterator first, InputIterator last, size_t n) {
   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;
@@ -627,7 +776,7 @@ whenN(InputIterator first, InputIterator last, size_t n) {
         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)));
         }
       }
     });
@@ -643,140 +792,51 @@ whenN(InputIterator first, InputIterator last, size_t n) {
   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>
@@ -803,22 +863,17 @@ Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
   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));
     }
   });
 
@@ -828,14 +883,200 @@ Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
 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