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
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));
+}
+
}}
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.
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>
}
};
+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
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) {
}
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);
+}