add EXPECT_THROW_RE() and EXPECT_THROW_ERRNO() test macros
[folly.git] / folly / futures / helpers.h
index 7f074a06664a5a363db0cb64508d3e7b3ae2d4c3..9b5e85ba1f0ac96ede79a336389f34faa9645e63 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 #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
@@ -59,6 +110,73 @@ namespace futures {
 
 } // namespace futures
 
+/**
+  Make a completed SemiFuture by moving in a value. e.g.
+
+    string foo = "foo";
+    auto f = makeSemiFuture(std::move(foo));
+
+  or
+
+    auto f = makeSemiFuture<string>("foo");
+*/
+template <class T>
+SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t);
+
+/** Make a completed void SemiFuture. */
+SemiFuture<Unit> makeSemiFuture();
+
+/**
+  Make a SemiFuture by executing a function.
+
+  If the function returns a value of type T, makeSemiFutureWith
+  returns a completed SemiFuture<T>, capturing the value returned
+  by the function.
+
+  If the function returns a SemiFuture<T> already, makeSemiFutureWith
+  returns just that.
+
+  Either way, if the function throws, a failed Future is
+  returned that captures the exception.
+*/
+
+// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
+template <class F>
+typename std::enable_if<isSemiFuture<typename std::result_of<F()>::type>::value,
+                        typename std::result_of<F()>::type>::type
+makeSemiFutureWith(F&& func);
+
+// makeSemiFutureWith(T()) -> SemiFuture<T>
+// makeSemiFutureWith(void()) -> SemiFuture<Unit>
+template <class F>
+typename std::enable_if<
+    !(isSemiFuture<typename std::result_of<F()>::type>::value),
+    SemiFuture<typename Unit::Lift<typename std::result_of<F()>::type>::type>>::type
+makeSemiFutureWith(F&& func);
+
+/// Make a failed Future from an exception_ptr.
+/// Because the Future's type cannot be inferred you have to specify it, e.g.
+///
+///   auto f = makeSemiFuture<string>(std::current_exception());
+template <class T>
+FOLLY_DEPRECATED("use makeSemiFuture(exception_wrapper)")
+SemiFuture<T> makeSemiFuture(std::exception_ptr const& e);
+
+/// Make a failed SemiFuture from an exception_wrapper.
+template <class T>
+SemiFuture<T> makeSemiFuture(exception_wrapper ew);
+
+/** Make a SemiFuture from an exception type E that can be passed to
+  std::make_exception_ptr(). */
+template <class T, class E>
+typename std::enable_if<std::is_base_of<std::exception, E>::value,
+                        SemiFuture<T>>::type
+makeSemiFuture(E const& e);
+
+/** Make a Future out of a Try */
+template <class T>
+SemiFuture<T> makeSemiFuture(Try<T>&& t);
+
 /**
   Make a completed Future by moving in a value. e.g.
 
@@ -148,7 +266,7 @@ inline Future<Unit> via(
 /// easier to read and slightly more efficient.
 template <class Func>
 auto via(Executor*, Func&& func)
-  -> Future<typename isFuture<decltype(func())>::Inner>;
+    -> Future<typename isFuture<decltype(std::declval<Func>()())>::Inner>;
 
 /** When all the input Futures complete, the returned Future will complete.
   Errors do not cause early termination; this Future will always succeed
@@ -181,17 +299,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 +321,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 +387,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 +441,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,73 +460,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.
- */
-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