Add window overload that takes an executor to prevent stack overflow
[folly.git] / folly / futures / helpers.h
index 26187b8fa0662316bf66ca97471edb7c3364c854..a5198e714be908099a743c6b0da507e9fc146ae3 100644 (file)
  */
 #pragma once
 
-#include <folly/futures/Future.h>
+#include <atomic>
+#include <tuple>
+#include <utility>
+
 #include <folly/Portability.h>
+#include <folly/Try.h>
+#include <folly/futures/Future.h>
+#include <folly/futures/Promise.h>
 
 namespace folly {
 
+namespace futures {
+namespace detail {
+template <typename... Ts>
+struct CollectAllVariadicContext {
+  CollectAllVariadicContext() {}
+  template <typename T, size_t I>
+  inline void setPartialResult(Try<T>& t) {
+    std::get<I>(results) = std::move(t);
+  }
+  ~CollectAllVariadicContext() {
+    p.setValue(std::move(results));
+  }
+  Promise<std::tuple<Try<Ts>...>> p;
+  std::tuple<Try<Ts>...> results;
+  typedef Future<std::tuple<Try<Ts>...>> type;
+};
+
+template <typename... Ts>
+struct CollectVariadicContext {
+  CollectVariadicContext() {}
+  template <typename T, size_t I>
+  inline void setPartialResult(Try<T>& t) {
+    if (t.hasException()) {
+      if (!threw.exchange(true)) {
+        p.setException(std::move(t.exception()));
+      }
+    } else if (!threw) {
+      std::get<I>(results) = std::move(t);
+    }
+  }
+  ~CollectVariadicContext() noexcept {
+    if (!threw.exchange(true)) {
+      p.setValue(unwrapTryTuple(std::move(results)));
+    }
+  }
+  Promise<std::tuple<Ts...>> p;
+  std::tuple<folly::Try<Ts>...> results;
+  std::atomic<bool> threw{false};
+  typedef Future<std::tuple<Ts...>> type;
+};
+} // namespace detail
+} // namespace futures
+
 /// This namespace is for utility functions that would usually be static
 /// members of Future, except they don't make sense there because they don't
 /// depend on the template type (rather, on the type of their arguments in
@@ -44,10 +93,12 @@ namespace futures {
    * Set func as the callback for each input Future and return a vector of
    * Futures containing the results in the input order.
    */
-  template <class It, class F,
-            class ItT = typename std::iterator_traits<It>::value_type,
-            class Result
-      = typename decltype(std::declval<ItT>().then(std::declval<F>()))::value_type>
+  template <
+      class It,
+      class F,
+      class ItT = typename std::iterator_traits<It>::value_type,
+      class Result = typename decltype(
+          std::declval<ItT>().then(std::declval<F>()))::value_type>
   std::vector<Future<Result>> map(It first, It last, F func);
 
   // Sugar for the most common case
@@ -181,17 +232,16 @@ auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {
 /// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>.
 /// The Futures are moved in, so your copies are invalid.
 template <typename... Fs>
-typename detail::CollectAllVariadicContext<
-  typename std::decay<Fs>::type::value_type...>::type
+typename futures::detail::CollectAllVariadicContext<
+    typename std::decay<Fs>::type::value_type...>::type
 collectAll(Fs&&... fs);
 
 /// Like collectAll, but will short circuit on the first exception. Thus, the
 /// type of the returned Future is std::vector<T> instead of
 /// std::vector<Try<T>>
 template <class InputIterator>
-Future<typename detail::CollectContext<
-  typename std::iterator_traits<InputIterator>::value_type::value_type
->::result_type>
+Future<typename futures::detail::CollectContext<typename std::iterator_traits<
+    InputIterator>::value_type::value_type>::result_type>
 collect(InputIterator first, InputIterator last);
 
 /// Sugar for the most common case
@@ -204,8 +254,8 @@ auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {
 /// type of the returned Future is std::tuple<T1, T2, ...> instead of
 /// std::tuple<Try<T1>, Try<T2>, ...>
 template <typename... Fs>
-typename detail::CollectVariadicContext<
-  typename std::decay<Fs>::type::value_type...>::type
+typename futures::detail::CollectVariadicContext<
+    typename std::decay<Fs>::type::value_type...>::type
 collect(Fs&&... fs);
 
 /** The result is a pair of the index of the first Future to complete and
@@ -270,18 +320,30 @@ auto collectN(Collection&& c, size_t n)
 
     func must return a Future for each value in input
   */
-template <class Collection, class F,
-          class ItT = typename std::iterator_traits<
-            typename Collection::iterator>::value_type,
-          class Result = typename detail::resultOf<F, ItT&&>::value_type>
+template <
+    class Collection,
+    class F,
+    class ItT = typename std::iterator_traits<
+        typename Collection::iterator>::value_type,
+    class Result = typename futures::detail::resultOf<F, ItT&&>::value_type>
+std::vector<Future<Result>> window(Collection input, F func, size_t n);
+
+template <
+    class Collection,
+    class F,
+    class ItT = typename std::iterator_traits<
+        typename Collection::iterator>::value_type,
+    class Result = typename futures::detail::resultOf<F, ItT&&>::value_type>
 std::vector<Future<Result>>
-window(Collection input, F func, size_t n);
+window(Executor* executor, Collection input, F func, size_t n);
 
 template <typename F, typename T, typename ItT>
 using MaybeTryArg = typename std::conditional<
-  detail::callableWith<F, T&&, Try<ItT>&&>::value, Try<ItT>, ItT>::type;
+    futures::detail::callableWith<F, T&&, Try<ItT>&&>::value,
+    Try<ItT>,
+    ItT>::type;
 
-template<typename F, typename T, typename Arg>
+template <typename F, typename T, typename Arg>
 using isFutureResult = isFuture<typename std::result_of<F(T&&, Arg&&)>::type>;
 
 /** repeatedly calls func on every result, e.g.
@@ -312,9 +374,12 @@ auto reduce(Collection&& c, T&& initial, F&& func)
 /** like reduce, but calls func on finished futures as they complete
     does NOT keep the order of the input
   */
-template <class It, class T, class F,
-          class ItT = typename std::iterator_traits<It>::value_type::value_type,
-          class Arg = MaybeTryArg<F, T, ItT>>
+template <
+    class It,
+    class T,
+    class F,
+    class ItT = typename std::iterator_traits<It>::value_type::value_type,
+    class Arg = MaybeTryArg<F, T, ItT>>
 Future<T> unorderedReduce(It first, It last, T initial, F func);
 
 /// Sugar for the most common case
@@ -328,76 +393,4 @@ auto unorderedReduce(Collection&& c, T&& initial, F&& func)
       std::forward<T>(initial),
       std::forward<F>(func));
 }
-
-namespace futures {
-
-/**
- *  retrying
- *
- *  Given a policy and a future-factory, creates futures according to the
- *  policy.
- *
- *  The policy must be moveable - retrying will move it a lot - and callable of
- *  either of the two forms:
- *  - Future<bool>(size_t, exception_wrapper)
- *  - bool(size_t, exception_wrapper)
- *  Internally, the latter is transformed into the former in the obvious way.
- *  The first parameter is the attempt number of the next prospective attempt;
- *  the second parameter is the most recent exception. The policy returns a
- *  Future<bool> which, when completed with true, indicates that a retry is
- *  desired.
- *
- *  We provide a few generic policies:
- *  - Basic
- *  - CappedJitteredexponentialBackoff
- *
- *  Custom policies may use the most recent try number and exception to decide
- *  whether to retry and optionally to do something interesting like delay
- *  before the retry. Users may pass inline lambda expressions as policies, or
- *  may define their own data types meeting the above requirements. Users are
- *  responsible for managing the lifetimes of anything pointed to or referred to
- *  from inside the policy.
- *
- *  For example, one custom policy may try up to k times, but only if the most
- *  recent exception is one of a few types or has one of a few error codes
- *  indicating that the failure was transitory.
- *
- *  Cancellation is not supported.
- *
- *  If both FF and Policy inline executes, then it is possible to hit a stack
- *  overflow due to the recursive nature of the retry implementation
- */
-template <class Policy, class FF>
-typename std::result_of<FF(size_t)>::type
-retrying(Policy&& p, FF&& ff);
-
-/**
- *  generic retrying policies
- */
-
-inline
-std::function<bool(size_t, const exception_wrapper&)>
-retryingPolicyBasic(
-    size_t max_tries);
-
-template <class Policy, class URNG>
-std::function<Future<bool>(size_t, const exception_wrapper&)>
-retryingPolicyCappedJitteredExponentialBackoff(
-    size_t max_tries,
-    Duration backoff_min,
-    Duration backoff_max,
-    double jitter_param,
-    URNG&& rng,
-    Policy&& p);
-
-inline
-std::function<Future<bool>(size_t, const exception_wrapper&)>
-retryingPolicyCappedJitteredExponentialBackoff(
-    size_t max_tries,
-    Duration backoff_min,
-    Duration backoff_max,
-    double jitter_param);
-
-}
-
-} // namespace
+} // namespace folly