Commit df642f04 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Split folly::futures::map over value and Try forms

Summary: Splits folly::futures::map over forms that take value continuations and try continuations. This allows the implementation to call Future::thenValue and Future::thenTry correctly.

Reviewed By: yfeldblum

Differential Revision: D10861465

fbshipit-source-id: 31044127f43e574edcc01c6bb173ecc4f0c9c90e
parent 90ae95ed
......@@ -2354,28 +2354,44 @@ Future<Unit> times(const int n, F&& thunk) {
}
namespace futures {
template <class It, class F, class ItT, class Result>
std::vector<Future<Result>> map(It first, It last, F func) {
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>> mapValue(It first, It last, F func) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
FOLLY_PUSH_WARNING
FOLLY_GNU_DISABLE_WARNING("-Wdeprecated-declarations")
results.push_back(std::move(*it).then(func));
FOLLY_POP_WARNING
results.push_back(std::move(*it).thenValue(func));
}
return results;
}
template <class It, class F, class ItT, class Result>
std::vector<Future<Result>> map(Executor& exec, It first, It last, F func) {
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>> mapTry(It first, It last, F func, int) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
FOLLY_PUSH_WARNING
FOLLY_GNU_DISABLE_WARNING("-Wdeprecated-declarations")
results.push_back(std::move(*it).via(&exec).then(func));
FOLLY_POP_WARNING
results.push_back(std::move(*it).thenTry(func));
}
return results;
}
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>>
mapValue(Executor& exec, It first, It last, F func) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
results.push_back(std::move(*it).via(&exec).thenValue(func));
}
return results;
}
template <class It, class F, class ItT, class Tag, class Result>
std::vector<Future<Result>>
mapTry(Executor& exec, It first, It last, F func, int) {
std::vector<Future<Result>> results;
results.reserve(std::distance(first, last));
for (auto it = first; it != last; it++) {
results.push_back(std::move(*it).via(&exec).thenTry(func));
}
return results;
}
......
......@@ -55,9 +55,25 @@ template <
class It,
class F,
class ItT = typename std::iterator_traits<It>::value_type,
class Tag =
std::enable_if_t<is_invocable<F, typename ItT::value_type&&>::value>,
class Result = typename decltype(
std::declval<ItT>().then(std::declval<F>()))::value_type>
std::vector<Future<Result>> map(It first, It last, F func);
std::declval<ItT>().thenValue(std::declval<F>()))::value_type>
std::vector<Future<Result>> mapValue(It first, It last, F func);
/**
* Set func as the callback for each input Future and return a vector of
* Futures containing the results in the input order.
*/
template <
class It,
class F,
class ItT = typename std::iterator_traits<It>::value_type,
class Tag =
std::enable_if_t<!is_invocable<F, typename ItT::value_type&&>::value>,
class Result = typename decltype(
std::declval<ItT>().thenTry(std::declval<F>()))::value_type>
std::vector<Future<Result>> mapTry(It first, It last, F func, int = 0);
/**
* Set func as the callback for each input Future and return a vector of
......@@ -68,22 +84,56 @@ template <
class It,
class F,
class ItT = typename std::iterator_traits<It>::value_type,
class Result = typename decltype(std::move(std::declval<ItT>())
.via(std::declval<Executor*>())
.then(std::declval<F>()))::value_type>
std::vector<Future<Result>> map(Executor& exec, It first, It last, F func);
class Tag =
std::enable_if_t<is_invocable<F, typename ItT::value_type&&>::value>,
class Result =
typename decltype(std::move(std::declval<ItT>())
.via(std::declval<Executor*>())
.thenValue(std::declval<F>()))::value_type>
std::vector<Future<Result>> mapValue(Executor& exec, It first, It last, F func);
/**
* Set func as the callback for each input Future and return a vector of
* Futures containing the results in the input order and completing on
* exec.
*/
template <
class It,
class F,
class ItT = typename std::iterator_traits<It>::value_type,
class Tag =
std::enable_if_t<!is_invocable<F, typename ItT::value_type&&>::value>,
class Result =
typename decltype(std::move(std::declval<ItT>())
.via(std::declval<Executor*>())
.thenTry(std::declval<F>()))::value_type>
std::vector<Future<Result>>
mapTry(Executor& exec, It first, It last, F func, int = 0);
// Sugar for the most common case
template <class Collection, class F>
auto map(Collection&& c, F&& func) -> decltype(map(c.begin(), c.end(), func)) {
return map(c.begin(), c.end(), std::forward<F>(func));
auto mapValue(Collection&& c, F&& func)
-> decltype(mapValue(c.begin(), c.end(), func)) {
return mapValue(c.begin(), c.end(), std::forward<F>(func));
}
template <class Collection, class F>
auto mapTry(Collection&& c, F&& func)
-> decltype(mapTry(c.begin(), c.end(), func)) {
return mapTry(c.begin(), c.end(), std::forward<F>(func));
}
// Sugar for the most common case
template <class Collection, class F>
auto map(Executor& exec, Collection&& c, F&& func)
-> decltype(map(exec, c.begin(), c.end(), func)) {
return map(exec, c.begin(), c.end(), std::forward<F>(func));
auto mapValue(Executor& exec, Collection&& c, F&& func)
-> decltype(mapValue(exec, c.begin(), c.end(), func)) {
return mapValue(exec, c.begin(), c.end(), std::forward<F>(func));
}
template <class Collection, class F>
auto mapTry(Executor& exec, Collection&& c, F&& func)
-> decltype(mapTry(exec, c.begin(), c.end(), func)) {
return mapTry(exec, c.begin(), c.end(), std::forward<F>(func));
}
} // namespace futures
......
......@@ -363,8 +363,8 @@ void complexBenchmark() {
collect(fsGen<T>());
collectAll(fsGen<T>());
collectAny(fsGen<T>());
futures::map(fsGen<T>(), [](const T& t) { return t; });
futures::map(fsGen<T>(), [](const T& t) { return makeFuture(T(t)); });
futures::mapValue(fsGen<T>(), [](const T& t) { return t; });
futures::mapValue(fsGen<T>(), [](const T& t) { return makeFuture(T(t)); });
}
BENCHMARK_DRAW_LINE();
......
......@@ -30,7 +30,32 @@ TEST(Map, basic) {
fs.push_back(p3.getFuture());
int c = 0;
std::vector<Future<Unit>> fs2 = futures::map(fs, [&](int i) { c += i; });
std::vector<Future<Unit>> fs2 = futures::mapValue(fs, [&](int i) { c += i; });
// Ensure we call the callbacks as the futures complete regardless of order
p2.setValue(1);
EXPECT_EQ(1, c);
p3.setValue(1);
EXPECT_EQ(2, c);
p1.setValue(1);
EXPECT_EQ(3, c);
EXPECT_TRUE(collect(fs2).isReady());
}
TEST(Map, basic_try) {
Promise<int> p1;
Promise<int> p2;
Promise<int> p3;
std::vector<Future<int>> fs;
fs.push_back(p1.getFuture());
fs.push_back(p2.getFuture());
fs.push_back(p3.getFuture());
int c = 0;
std::vector<Future<Unit>> fs2 =
futures::mapTry(fs, [&](folly::Try<int> i) { c += i.value(); });
// Ensure we call the callbacks as the futures complete regardless of order
p2.setValue(1);
......@@ -56,7 +81,33 @@ TEST(Map, executor) {
int c = 0;
std::vector<Future<Unit>> fs2 =
futures::map(exec, fs, [&](int i) { c += i; });
futures::mapValue(exec, fs, [&](int i) { c += i; });
// Ensure we call the callbacks as the futures complete regardless of order
p2.setValue(1);
EXPECT_EQ(1, c);
p3.setValue(1);
EXPECT_EQ(2, c);
p1.setValue(1);
EXPECT_EQ(3, c);
EXPECT_TRUE(collect(fs2).isReady());
}
TEST(Map, executor_try) {
Promise<int> p1;
Promise<int> p2;
Promise<int> p3;
folly::InlineExecutor exec;
std::vector<Future<int>> fs;
fs.push_back(p1.getFuture());
fs.push_back(p2.getFuture());
fs.push_back(p3.getFuture());
int c = 0;
std::vector<Future<Unit>> fs2 =
futures::mapTry(exec, fs, [&](folly::Try<int> i) { c += i.value(); });
// Ensure we call the callbacks as the futures complete regardless of order
p2.setValue(1);
......@@ -82,7 +133,7 @@ TEST(Map, semifuture) {
int c = 0;
std::vector<Future<Unit>> fs2 =
futures::map(exec, fs, [&](int i) { c += i; });
futures::mapValue(exec, fs, [&](int i) { c += i; });
// Ensure we call the callbacks as the futures complete regardless of order
p2.setValue(1);
......
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