add waitWithSemaphore to folly::wangle
[folly.git] / folly / wangle / Future.h
index d89f7134092f538c6f957ff1dd8baf5be3d60118..a0030f84b3e669556be55c0eafac126dcb86a287 100644 (file)
@@ -21,6 +21,7 @@
 #include <functional>
 #include <memory>
 #include <type_traits>
+#include <vector>
 
 #include "folly/MoveWrapper.h"
 #include "Promise.h"
@@ -58,8 +59,23 @@ class Future {
   typename std::add_lvalue_reference<const T>::type
   value() const;
 
+  /// Returns a future which will call back on the other side of executor.
+  ///
+  ///   f.via(e).then(a); // safe
+  ///
+  ///   f.via(e).then(a).then(b); // faux pas
+  ///
+  /// a will definitely execute in the intended thread, but b may execute
+  /// either in that thread, or in the current thread. If you need to
+  /// guarantee where b executes, use a Later.
   template <typename Executor>
-  Future<T> executeWithSameThread(Executor* executor);
+  Future<T> via(Executor* executor);
+
+  /// Deprecated alias for via
+  template <typename Executor>
+  Future<T> executeWithSameThread(Executor* executor) {
+    return via(executor);
+  }
 
   /**
      Thread-safe version of executeWith
@@ -71,6 +87,8 @@ class Future {
      Instead, you may pass in a Promise so that we can set up
      the rest of the chain in advance, without any racey
      modifications of the continuation
+
+     Deprecated. Use a Later.
    */
   template <typename Executor>
   void executeWith(Executor* executor, Promise<T>&& cont_promise);
@@ -100,7 +118,7 @@ class Future {
     Future<typename std::result_of<F(Try<T>&&)>::type> >::type
   then(F&& func);
 
-  /// Variant where func returns a future<T> instead of a T. e.g.
+  /// Variant where func returns a Future<T> instead of a T. e.g.
   ///
   ///   Future<string> f2 = f1.then(
   ///     [](Try<T>&&) { return makeFuture<string>("foo"); });
@@ -110,6 +128,70 @@ class Future {
     Future<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
   then(F&& func);
 
+  /// Variant where func is an ordinary function (static method, method)
+  ///
+  ///   R doWork(Try<T>&&);
+  ///
+  ///   Future<R> f2 = f1.then(doWork);
+  ///
+  /// or
+  ///
+  ///   struct Worker {
+  ///     static R doWork(Try<T>&&); }
+  ///
+  ///   Future<R> f2 = f1.then(&Worker::doWork);
+  template <class = T, class R = std::nullptr_t>
+  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
+  inline then(R(*func)(Try<T>&&)) {
+    return then([func](Try<T>&& t) {
+      return (*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func returns a Future<R> instead of a R. e.g.
+  ///
+  ///   struct Worker {
+  ///     Future<R> doWork(Try<T>&&); }
+  ///
+  ///   Future<R> f2 = f1.then(&Worker::doWork);
+  template <class = T, class R = std::nullptr_t>
+  typename std::enable_if<isFuture<R>::value, R>::type
+  inline then(R(*func)(Try<T>&&)) {
+    return then([func](Try<T>&& t) {
+      return (*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func is an member function
+  ///
+  ///   struct Worker {
+  ///     R doWork(Try<T>&&); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<!isFuture<R>::value, Future<R>>::type
+  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
+    return then([instance, func](Try<T>&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
+  /// Variant where func returns a Future<R> instead of a R. e.g.
+  ///
+  ///   struct Worker {
+  ///     Future<R> doWork(Try<T>&&); }
+  ///
+  ///   Worker *w;
+  ///   Future<R> f2 = f1.then(w, &Worker::doWork);
+  template <class = T, class R = std::nullptr_t, class Caller = std::nullptr_t>
+  typename std::enable_if<isFuture<R>::value, R>::type
+  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
+    return then([instance, func](Try<T>&& t) {
+      return (instance->*func)(std::move(t));
+    });
+  }
+
   /// Convenience method for ignoring the value and creating a Future<void>.
   /// Exceptions still propagate.
   Future<void> then();
@@ -176,6 +258,10 @@ template <class T, class E>
 typename std::enable_if<std::is_base_of<std::exception, E>::value, Future<T>>::type
 makeFuture(E const& e);
 
+/** Make a Future out of a Try */
+template <class T>
+Future<T> makeFuture(Try<T>&& t);
+
 /** When all the input Futures complete, the returned Future will complete.
   Errors do not cause early termination; this Future will always succeed
   after all its Futures have finished (whether successfully or with an
@@ -227,6 +313,15 @@ Future<std::vector<std::pair<
   Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>>
 whenN(InputIterator first, InputIterator last, size_t n);
 
+/** Wait for the given future to complete on a semaphore. Returns the result of
+ * the given future.
+ *
+ * NB if the promise for the future would be fulfilled in the same thread that
+ * you call this, it will deadlock.
+ */
+template <class F>
+typename F::value_type waitWithSemaphore(F&& f);
+
 }} // folly::wangle
 
 #include "Future-inl.h"