Commit 2bfde4cd authored by Hannes Roth's avatar Hannes Roth Committed by Sara Golemon

(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
parent 940aec84
......@@ -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));
}
}}
......@@ -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>
......@@ -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
......@@ -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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment