From 2bfde4cdaf98e1d638bc92e6e705a7590cd67386 Mon Sep 17 00:00:00 2001 From: Hannes Roth Date: Thu, 4 Sep 2014 09:17:56 -0700 Subject: [PATCH] (Folly/Wangle) Later.then, Later.whenAllLater 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 | 30 ++++++++++++++++++++++ folly/wangle/Later.h | 25 +++++++++++++++++++ folly/wangle/detail/State.h | 9 +++++++ folly/wangle/test/LaterTest.cpp | 44 +++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/folly/wangle/Later-inl.h b/folly/wangle/Later-inl.h index 95d4461f..b054d6d5 100644 --- a/folly/wangle/Later-inl.h +++ b/folly/wangle/Later-inl.h @@ -174,4 +174,34 @@ Future Later::launch() { return std::move(*future_); } +template +Later>> whenAllLater(std::vector>&& laters) { + if (laters.size() == 0) { + return Later>>(std::vector>()); + } + + auto ctx = new detail::WhenAllLaterContext(); + ctx->total = laters.size(); + ctx->results.resize(ctx->total); + + MoveWrapper>> mlaters{std::move(laters)}; + + std::function>&&)>&&)> wrapper = + [ctx, mlaters](std::function>&&)>&& fn) mutable { + ctx->fn = std::move(fn); + size_t i = 0; + for (auto& l : *mlaters) { + l.then([ctx, i](Try&& 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::move(wrapper)); +} + }} diff --git a/folly/wangle/Later.h b/folly/wangle/Later.h index edde49d0..228e9737 100644 --- a/folly/wangle/Later.h +++ b/folly/wangle/Later.h @@ -175,6 +175,27 @@ class Later { Later&&)>::type::value_type> >::type then(F&& fn); + + /// Variant where func is an ordinary function (static method, method) + /// Must return a Later + template + typename std::enable_if::value, R>::type + inline then(R(*func)(Try&&)) { + return then([func](Try&& t) { + return (*func)(std::move(t)); + }); + } + + /// Variant where func is an member function + /// Must return a Later + template + typename std::enable_if::value, R>::type + inline then(Caller *instance, R(Caller::*func)(Try&&)) { + return then([instance, func](Try&& 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 +Later>> whenAllLater(std::vector>&& laters); + }} #include diff --git a/folly/wangle/detail/State.h b/folly/wangle/detail/State.h index b93e9ea6..491c38fa 100644 --- a/folly/wangle/detail/State.h +++ b/folly/wangle/detail/State.h @@ -236,4 +236,13 @@ struct WhenAnyContext { } }; +template +struct WhenAllLaterContext { + explicit WhenAllLaterContext() : count(0), total(0) {} + std::function>&&)> fn; + std::vector > results; + std::atomic count; + size_t total; +}; + }}} // namespace diff --git a/folly/wangle/test/LaterTest.cpp b/folly/wangle/test/LaterTest.cpp index b4cd96be..97dfd9fc 100644 --- a/folly/wangle/test/LaterTest.cpp +++ b/folly/wangle/test/LaterTest.cpp @@ -107,6 +107,29 @@ TEST(Later, then_future) { EXPECT_TRUE(future.value()); } +static Later doWorkStatic(Try&& t) { + return Later(t.value() + ";static"); +} + +TEST(Later, then_function) { + struct Worker { + Later doWork(Try&& t) { + return Later(t.value() + ";class"); + } + static Later doWorkStatic(Try&& t) { + return Later(t.value() + ";class-static"); + } + } w; + + auto f = Later(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&& 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> laters; + laters.emplace_back(Later(1).then([&](Try&& i) mutable { + done += i.value(); return 8; + })); + laters.emplace_back(Later(2).then([&](Try&& i) mutable { + done += i.value(); return 16; + })); + laters.emplace_back(Later(4).then([&](Try&& i) mutable { + done += i.value(); return 32; + })); + whenAllLater(std::move(laters)) + .then([&](Try>>&& v) mutable { + for (const auto& i : v.value()) { + done += i.value(); + } + }).launch(); + EXPECT_EQ(done, 63); +} -- 2.34.1