(Folly/Wangle) Later.then, Later.whenAllLater
authorHannes Roth <hannesr@fb.com>
Thu, 4 Sep 2014 16:17:56 +0000 (09:17 -0700)
committerSara Golemon <sgolemon@fb.com>
Tue, 9 Sep 2014 21:22:23 +0000 (14:22 -0700)
Summary:
Adding some basic functionality to `Later` to make it easier to chain
things.

Test Plan:
Added tests.

`fbconfig -r folly/wangle && fbmake runtests_{dbg,dbgo,opt}`
`fbconfig --clang -r folly/wangle && fbmake runtests_{dbg,dbgo,opt}`

Reviewed By: hans@fb.com

Subscribers: fugalh, njormrod

FB internal diff: D1527826

Tasks: 4993420

folly/wangle/Later-inl.h
folly/wangle/Later.h
folly/wangle/detail/State.h
folly/wangle/test/LaterTest.cpp

index 95d4461fe615f1b71155256b495ca7a96e0bc22b..b054d6d54209e0ec70125f01d959471f95fc8324 100644 (file)
@@ -174,4 +174,34 @@ Future<T> Later<T>::launch() {
   return std::move(*future_);
 }
 
+template <class T>
+Later<std::vector<Try<T>>> whenAllLater(std::vector<Later<T>>&& laters) {
+  if (laters.size() == 0) {
+    return Later<std::vector<Try<T>>>(std::vector<Try<T>>());
+  }
+
+  auto ctx = new detail::WhenAllLaterContext<T>();
+  ctx->total = laters.size();
+  ctx->results.resize(ctx->total);
+
+  MoveWrapper<std::vector<Later<T>>> mlaters{std::move(laters)};
+
+  std::function<void(std::function<void(std::vector<Try<T>>&&)>&&)> wrapper =
+    [ctx, mlaters](std::function<void(std::vector<Try<T>>&&)>&& fn) mutable {
+      ctx->fn = std::move(fn);
+      size_t i = 0;
+      for (auto& l : *mlaters) {
+        l.then([ctx, i](Try<T>&& t) {
+            ctx->results[i] = std::move(t);
+            if (++ctx->count == ctx->total) {
+              ctx->fn(std::move(ctx->results));
+              delete ctx;
+            }
+          }).launch();
+          ++i;
+      }
+    };
+  return Later<std::vector<Try<T>>>(std::move(wrapper));
+}
+
 }}
index edde49d09c3d6cbfa2112e1015b3e43e65959a92..228e9737308c69d1d1e6ee285e2f9a1c580350d6 100644 (file)
@@ -175,6 +175,27 @@ class Later {
     Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
   then(F&& fn);
 
+
+  /// Variant where func is an ordinary function (static method, method)
+  /// Must return a Later
+  template <class R>
+  typename std::enable_if<isLater<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
+  /// Must return a Later
+  template <class R, class Caller>
+  typename std::enable_if<isLater<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));
+    });
+  }
+
   /*
    * Resets the executor - all then() calls made after the call to via() will be
    * made in the new executor. The Executor must outlive.
@@ -206,6 +227,10 @@ class Later {
   friend class Later;
 };
 
+// See Future.whenAll
+template <class T>
+Later<std::vector<Try<T>>> whenAllLater(std::vector<Later<T>>&& laters);
+
 }}
 
 #include <folly/wangle/Later-inl.h>
index b93e9ea6a324413b98649bede5586f40276640fa..491c38faafc71caec3d3625f029c944f71c29705 100644 (file)
@@ -236,4 +236,13 @@ struct WhenAnyContext {
   }
 };
 
+template <typename T>
+struct WhenAllLaterContext {
+  explicit WhenAllLaterContext() : count(0), total(0) {}
+  std::function<void(std::vector<Try<T>>&&)> fn;
+  std::vector<Try<T> > results;
+  std::atomic<size_t> count;
+  size_t total;
+};
+
 }}} // namespace
index b4cd96be90ed9788c0147cb2022840099ab290e7..97dfd9fcee77c5028fa65850a73e19681d7999c2 100644 (file)
@@ -107,6 +107,29 @@ TEST(Later, then_future) {
   EXPECT_TRUE(future.value());
 }
 
+static Later<std::string> doWorkStatic(Try<std::string>&& t) {
+  return Later<std::string>(t.value() + ";static");
+}
+
+TEST(Later, then_function) {
+  struct Worker {
+    Later<std::string> doWork(Try<std::string>&& t) {
+      return Later<std::string>(t.value() + ";class");
+    }
+    static Later<std::string> doWorkStatic(Try<std::string>&& t) {
+      return Later<std::string>(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = Later<std::string>(std::string("start"))
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&w, &Worker::doWork)
+    .launch();
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
 TEST_F(LaterFixture, thread_hops) {
   auto westThreadId = std::this_thread::get_id();
   auto future = later.via(eastExecutor.get()).then([=](Try<void>&& t) {
@@ -164,3 +187,24 @@ TEST_F(LaterFixture, chain_laters) {
   }
   EXPECT_EQ(future.value(), 1);
 }
+
+TEST(Later, when_all_later) {
+  size_t done = 0;
+  std::vector<Later<int>> laters;
+  laters.emplace_back(Later<int>(1).then([&](Try<int>&& i) mutable {
+    done += i.value(); return 8;
+  }));
+  laters.emplace_back(Later<int>(2).then([&](Try<int>&& i) mutable {
+    done += i.value(); return 16;
+  }));
+  laters.emplace_back(Later<int>(4).then([&](Try<int>&& i) mutable {
+    done += i.value(); return 32;
+  }));
+  whenAllLater(std::move(laters))
+  .then([&](Try<std::vector<Try<int>>>&& v) mutable {
+    for (const auto& i : v.value()) {
+      done += i.value();
+    }
+  }).launch();
+  EXPECT_EQ(done, 63);
+}