Commit d0df6d1f authored by Lee Howes's avatar Lee Howes Committed by Facebook GitHub Bot

Make collect* return SemiFuture

Summary:
Migration from Future-returning executor-erasing collectX forms to SemiFuture-returning forms, that are less risky in particular with coroutines.

Earlier changes added collectXSemiFuture and collectXUnsafe as a migration path. This diff changes the behaviour of collectX to return SemiFuture.

Reviewed By: yfeldblum

Differential Revision: D20260537

fbshipit-source-id: 9e01536e4d9af68ff429f9fcd10aea4881d62041
parent 63724d67
...@@ -1374,7 +1374,7 @@ void stealDeferredExecutors( ...@@ -1374,7 +1374,7 @@ void stealDeferredExecutors(
template <typename... Fs> template <typename... Fs>
SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
collectAllSemiFuture(Fs&&... fs) { collectAll(Fs&&... fs) {
using Result = std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>; using Result = std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>;
struct Context { struct Context {
~Context() { ~Context() {
...@@ -1409,9 +1409,9 @@ collectAllSemiFuture(Fs&&... fs) { ...@@ -1409,9 +1409,9 @@ collectAllSemiFuture(Fs&&... fs) {
} }
template <typename... Fs> template <typename... Fs>
Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> collectAll( SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
Fs&&... fs) { collectAllSemiFuture(Fs&&... fs) {
return collectAllSemiFuture(std::forward<Fs>(fs)...).toUnsafeFuture(); return collectAll(std::forward<Fs>(fs)...);
} }
template <typename... Fs> template <typename... Fs>
...@@ -1425,7 +1425,7 @@ collectAllUnsafe(Fs&&... fs) { ...@@ -1425,7 +1425,7 @@ collectAllUnsafe(Fs&&... fs) {
template <class InputIterator> template <class InputIterator>
SemiFuture<std::vector< SemiFuture<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAllSemiFuture(InputIterator first, InputIterator last) { collectAll(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type; using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type; using T = typename F::value_type;
...@@ -1473,23 +1473,22 @@ template <class InputIterator> ...@@ -1473,23 +1473,22 @@ template <class InputIterator>
Future<std::vector< Future<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAllUnsafe(InputIterator first, InputIterator last) { collectAllUnsafe(InputIterator first, InputIterator last) {
return collectAllSemiFuture(first, last).toUnsafeFuture(); return collectAll(first, last).toUnsafeFuture();
} }
template <class InputIterator> template <class InputIterator>
Future<std::vector< SemiFuture<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAll(InputIterator first, InputIterator last) { collectAllSemiFuture(InputIterator first, InputIterator last) {
return collectAllSemiFuture(first, last).toUnsafeFuture(); return collectAll(first, last);
} }
// collect (iterator) // collect (iterator)
// TODO(T26439406): Make return SemiFuture
template <class InputIterator> template <class InputIterator>
SemiFuture<std::vector< SemiFuture<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>> typename std::iterator_traits<InputIterator>::value_type::value_type>>
collectSemiFuture(InputIterator first, InputIterator last) { collect(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type; using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type; using T = typename F::value_type;
...@@ -1546,22 +1545,21 @@ template <class InputIterator> ...@@ -1546,22 +1545,21 @@ template <class InputIterator>
Future<std::vector< Future<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>> typename std::iterator_traits<InputIterator>::value_type::value_type>>
collectUnsafe(InputIterator first, InputIterator last) { collectUnsafe(InputIterator first, InputIterator last) {
return collectSemiFuture(first, last).toUnsafeFuture(); return collect(first, last).toUnsafeFuture();
} }
template <class InputIterator> template <class InputIterator>
Future<std::vector< SemiFuture<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>> typename std::iterator_traits<InputIterator>::value_type::value_type>>
collect(InputIterator first, InputIterator last) { collectSemiFuture(InputIterator first, InputIterator last) {
return collectSemiFuture(first, last).toUnsafeFuture(); return collect(first, last);
} }
// collect (variadic) // collect (variadic)
// TODO(T26439406): Make return SemiFuture
template <typename... Fs> template <typename... Fs>
SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(
collectSemiFuture(Fs&&... fs) { Fs&&... fs) {
using Result = std::tuple<typename remove_cvref_t<Fs>::value_type...>; using Result = std::tuple<typename remove_cvref_t<Fs>::value_type...>;
struct Context { struct Context {
~Context() { ~Context() {
...@@ -1605,15 +1603,15 @@ collectSemiFuture(Fs&&... fs) { ...@@ -1605,15 +1603,15 @@ collectSemiFuture(Fs&&... fs) {
} }
template <typename... Fs> template <typename... Fs>
Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect( SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>>
Fs&&... fs) { collectSemiFuture(Fs&&... fs) {
return collectSemiFuture(std::forward<Fs>(fs)...).toUnsafeFuture(); return collect(std::forward<Fs>(fs)...);
} }
template <typename... Fs> template <typename... Fs>
Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collectUnsafe( Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collectUnsafe(
Fs&&... fs) { Fs&&... fs) {
return collectSemiFuture(std::forward<Fs>(fs)...).toUnsafeFuture(); return collect(std::forward<Fs>(fs)...).toUnsafeFuture();
} }
template <class Collection> template <class Collection>
...@@ -1626,11 +1624,11 @@ auto collectSemiFuture(Collection&& c) ...@@ -1626,11 +1624,11 @@ auto collectSemiFuture(Collection&& c)
// TODO(T26439406): Make return SemiFuture // TODO(T26439406): Make return SemiFuture
template <class InputIterator> template <class InputIterator>
Future<std::pair< SemiFuture<std::pair<
size_t, size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAny(InputIterator first, InputIterator last) { collectAnySemiFuture(InputIterator first, InputIterator last) {
return collectAnySemiFuture(first, last).via(&InlineExecutor::instance()); return collectAny(first, last);
} }
template <class InputIterator> template <class InputIterator>
...@@ -1638,14 +1636,14 @@ Future<std::pair< ...@@ -1638,14 +1636,14 @@ Future<std::pair<
size_t, size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAnyUnsafe(InputIterator first, InputIterator last) { collectAnyUnsafe(InputIterator first, InputIterator last) {
return collectAnySemiFuture(first, last).via(&InlineExecutor::instance()); return collectAny(first, last).toUnsafeFuture();
} }
template <class InputIterator> template <class InputIterator>
SemiFuture<std::pair< SemiFuture<std::pair<
size_t, size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAnySemiFuture(InputIterator first, InputIterator last) { collectAny(InputIterator first, InputIterator last) {
using F = typename std::iterator_traits<InputIterator>::value_type; using F = typename std::iterator_traits<InputIterator>::value_type;
using T = typename F::value_type; using T = typename F::value_type;
......
...@@ -2352,7 +2352,12 @@ auto via(Executor::KeepAlive<>, Func&& func) -> Future< ...@@ -2352,7 +2352,12 @@ auto via(Executor::KeepAlive<>, Func&& func) -> Future<
follow with `via(executor)` because it will complete in whichever thread the follow with `via(executor)` because it will complete in whichever thread the
last Future completes in. last Future completes in.
The return type for Future<T> input is a Future<std::vector<Try<T>>> The return type for Future<T> input is a SemiFuture<std::vector<Try<T>>>
for collectX and collectXSemiFuture.
collectXUnsafe returns an inline Future that erases the executor from the
incoming Futures/SemiFutures. collectXUnsafe should be phased out and
replaced with collectX(...).via(e) where e is a valid non-inline executor.
*/ */
template <class InputIterator> template <class InputIterator>
SemiFuture<std::vector< SemiFuture<std::vector<
...@@ -2366,11 +2371,13 @@ auto collectAllSemiFuture(Collection&& c) ...@@ -2366,11 +2371,13 @@ auto collectAllSemiFuture(Collection&& c)
return collectAllSemiFuture(c.begin(), c.end()); return collectAllSemiFuture(c.begin(), c.end());
} }
// Unsafe variant, see above comment for details
template <class InputIterator> template <class InputIterator>
Future<std::vector< Future<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAllUnsafe(InputIterator first, InputIterator last); collectAllUnsafe(InputIterator first, InputIterator last);
// Unsafe variant sugar, see above comment for details
template <class Collection> template <class Collection>
auto collectAllUnsafe(Collection&& c) auto collectAllUnsafe(Collection&& c)
-> decltype(collectAllUnsafe(c.begin(), c.end())) { -> decltype(collectAllUnsafe(c.begin(), c.end())) {
...@@ -2378,7 +2385,7 @@ auto collectAllUnsafe(Collection&& c) ...@@ -2378,7 +2385,7 @@ auto collectAllUnsafe(Collection&& c)
} }
template <class InputIterator> template <class InputIterator>
Future<std::vector< SemiFuture<std::vector<
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAll(InputIterator first, InputIterator last); collectAll(InputIterator first, InputIterator last);
...@@ -2389,51 +2396,56 @@ auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) { ...@@ -2389,51 +2396,56 @@ auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) {
/// This version takes a varying number of Futures instead of an iterator. /// This version takes a varying number of Futures instead of an iterator.
/// The return type for (Future<T1>, Future<T2>, ...) input /// The return type for (Future<T1>, Future<T2>, ...) input
/// is a Future<std::tuple<Try<T1>, Try<T2>, ...>>. /// is a SemiFuture<std::tuple<Try<T1>, Try<T2>, ...>>.
/// The Futures are moved in, so your copies are invalid. /// The Futures are moved in, so your copies are invalid.
template <typename... Fs> template <typename... Fs>
SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
collectAllSemiFuture(Fs&&... fs); collectAllSemiFuture(Fs&&... fs);
// Unsafe variant of collectAll, see coment above for details. Returns
// a Future<std::tuple<Try<T1>, Try<T2>, ...>> on the Inline executor.
template <typename... Fs> template <typename... Fs>
Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
collectAllUnsafe(Fs&&... fs); collectAllUnsafe(Fs&&... fs);
template <typename... Fs> template <typename... Fs>
Future<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>> collectAll( SemiFuture<std::tuple<Try<typename remove_cvref_t<Fs>::value_type>...>>
Fs&&... fs); collectAll(Fs&&... fs);
/// Like collectAll, but will short circuit on the first exception. Thus, the /// Like collectAll, but will short circuit on the first exception. Thus, the
/// type of the returned Future is std::vector<T> instead of /// type of the returned SemiFuture is std::vector<T> instead of
/// std::vector<Try<T>> /// std::vector<Try<T>>
template <class InputIterator> template <class InputIterator>
Future<std::vector< SemiFuture<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>> typename std::iterator_traits<InputIterator>::value_type::value_type>>
collectUnsafe(InputIterator first, InputIterator last); collect(InputIterator first, InputIterator last);
/// Sugar for the most common case /// Sugar for the most common case
template <class Collection> template <class Collection>
auto collectUnsafe(Collection&& c) auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) {
-> decltype(collectUnsafe(c.begin(), c.end())) { return collect(c.begin(), c.end());
return collectUnsafe(c.begin(), c.end());
} }
// Unsafe variant of collect. Returns a Future<std::vector<T>> that
// completes inline.
template <class InputIterator> template <class InputIterator>
Future<std::vector< Future<std::vector<
typename std::iterator_traits<InputIterator>::value_type::value_type>> typename std::iterator_traits<InputIterator>::value_type::value_type>>
collect(InputIterator first, InputIterator last); collectUnsafe(InputIterator first, InputIterator last);
/// Sugar for the most common case /// Sugar for the most common unsafe case. Returns a Future<std::vector<T>>
// that completes inline.
template <class Collection> template <class Collection>
auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) { auto collectUnsafe(Collection&& c)
return collect(c.begin(), c.end()); -> decltype(collectUnsafe(c.begin(), c.end())) {
return collectUnsafe(c.begin(), c.end());
} }
/// Like collectAll, but will short circuit on the first exception. Thus, the /// Like collectAll, but will short circuit on the first exception. Thus, the
/// type of the returned Future is std::tuple<T1, T2, ...> instead of /// type of the returned SemiFuture is std::tuple<T1, T2, ...> instead of
/// std::tuple<Try<T1>, Try<T2>, ...> /// std::tuple<Try<T1>, Try<T2>, ...>
template <typename... Fs> template <typename... Fs>
Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect( SemiFuture<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(
Fs&&... fs); Fs&&... fs);
/** The result is a pair of the index of the first Future to complete and /** The result is a pair of the index of the first Future to complete and
...@@ -2443,10 +2455,11 @@ Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect( ...@@ -2443,10 +2455,11 @@ Future<std::tuple<typename remove_cvref_t<Fs>::value_type...>> collect(
This function is thread-safe for Futures running on different threads. This function is thread-safe for Futures running on different threads.
*/ */
template <class InputIterator> template <class InputIterator>
Future<std::pair< SemiFuture<std::pair<
size_t, size_t,
Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>> Try<typename std::iterator_traits<InputIterator>::value_type::value_type>>>
collectAny(InputIterator first, InputIterator last); collectAny(InputIterator first, InputIterator last);
// Unsafe variant of collectAny, Returns a Future that completes inline.
template <class InputIterator> template <class InputIterator>
Future<std::pair< Future<std::pair<
size_t, size_t,
...@@ -2463,6 +2476,8 @@ template <class Collection> ...@@ -2463,6 +2476,8 @@ template <class Collection>
auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) { auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) {
return collectAny(c.begin(), c.end()); return collectAny(c.begin(), c.end());
} }
// Unsafe variant of common form of collectAny, Returns a Future that completes
// inline.
template <class Collection> template <class Collection>
auto collectAnyUnsafe(Collection&& c) auto collectAnyUnsafe(Collection&& c)
-> decltype(collectAnyUnsafe(c.begin(), c.end())) { -> decltype(collectAnyUnsafe(c.begin(), c.end())) {
......
...@@ -79,7 +79,7 @@ TEST(Collect, collectAll) { ...@@ -79,7 +79,7 @@ TEST(Collect, collectAll) {
promises[3].setException(eggs); promises[3].setException(eggs);
EXPECT_TRUE(allf.isReady()); EXPECT_TRUE(allf.isReady());
EXPECT_FALSE(allf.getTry().hasException()); EXPECT_FALSE(allf.result().hasException());
auto& results = allf.value(); auto& results = allf.value();
EXPECT_EQ(42, results[0].value()); EXPECT_EQ(42, results[0].value());
...@@ -160,7 +160,7 @@ TEST(Collect, collectAllUnsafe) { ...@@ -160,7 +160,7 @@ TEST(Collect, collectAllUnsafe) {
promises[3].setException(eggs); promises[3].setException(eggs);
EXPECT_TRUE(allf.isReady()); EXPECT_TRUE(allf.isReady());
EXPECT_FALSE(allf.getTry().hasException()); EXPECT_FALSE(allf.result().hasException());
auto& results = allf.value(); auto& results = allf.value();
EXPECT_EQ(42, results[0].value()); EXPECT_EQ(42, results[0].value());
...@@ -531,7 +531,7 @@ TEST(Collect, collectAny) { ...@@ -531,7 +531,7 @@ TEST(Collect, collectAny) {
futures.push_back(p.getFuture()); futures.push_back(p.getFuture());
} }
auto anyf = collectAny(futures).thenValue( auto anyf = collectAnyUnsafe(futures).thenValue(
[](std::pair<size_t, Try<int>> p) { EXPECT_EQ(42, p.second.value()); }); [](std::pair<size_t, Try<int>> p) { EXPECT_EQ(42, p.second.value()); });
promises[3].setValue(42); promises[3].setValue(42);
...@@ -656,9 +656,10 @@ TEST(Collect, alreadyCompleted) { ...@@ -656,9 +656,10 @@ TEST(Collect, alreadyCompleted) {
fs.push_back(makeFuture(i)); fs.push_back(makeFuture(i));
} }
collectAny(fs).thenValue([&](std::pair<size_t, Try<int>> p) { collectAny(fs).toUnsafeFuture().thenValue(
EXPECT_EQ(p.first, p.second.value()); [&](std::pair<size_t, Try<int>> p) {
}); EXPECT_EQ(p.first, p.second.value());
});
} }
} }
...@@ -974,6 +975,7 @@ TEST(Collect, collectVariadic) { ...@@ -974,6 +975,7 @@ TEST(Collect, collectVariadic) {
Future<int> fi = pi.getFuture(); Future<int> fi = pi.getFuture();
bool flag = false; bool flag = false;
collect(std::move(fb), std::move(fi)) collect(std::move(fb), std::move(fi))
.toUnsafeFuture()
.thenValue([&](std::tuple<bool, int> tup) { .thenValue([&](std::tuple<bool, int> tup) {
flag = true; flag = true;
EXPECT_EQ(std::get<0>(tup), true); EXPECT_EQ(std::get<0>(tup), true);
...@@ -1013,7 +1015,7 @@ TEST(Collect, collectVariadicWithException) { ...@@ -1013,7 +1015,7 @@ TEST(Collect, collectVariadicWithException) {
EXPECT_FALSE(f.isReady()); EXPECT_FALSE(f.isReady());
pi.setException(eggs); pi.setException(eggs);
EXPECT_TRUE(f.isReady()); EXPECT_TRUE(f.isReady());
EXPECT_TRUE(f.getTry().hasException()); EXPECT_TRUE(f.result().hasException());
EXPECT_THROW(std::move(f).get(), eggs_t); EXPECT_THROW(std::move(f).get(), eggs_t);
} }
......
...@@ -95,14 +95,16 @@ TEST(Reduce, chain) { ...@@ -95,14 +95,16 @@ TEST(Reduce, chain) {
}; };
{ {
auto f = collectAll(makeFutures(3)).reduce(0, [](int a, Try<int>&& b) { auto f = collectAll(makeFutures(3))
return a + *b; .toUnsafeFuture()
}); .reduce(0, [](int a, Try<int>&& b) { return a + *b; });
EXPECT_EQ(6, std::move(f).get()); EXPECT_EQ(6, std::move(f).get());
} }
{ {
auto f = auto f =
collect(makeFutures(3)).reduce(0, [](int a, int&& b) { return a + b; }); collect(makeFutures(3)).toUnsafeFuture().reduce(0, [](int a, int&& b) {
return a + b;
});
EXPECT_EQ(6, std::move(f).get()); EXPECT_EQ(6, std::move(f).get());
} }
} }
......
...@@ -148,7 +148,7 @@ TEST(Wait, waitWithDuration) { ...@@ -148,7 +148,7 @@ TEST(Wait, waitWithDuration) {
vector<Future<bool>> v_fb; vector<Future<bool>> v_fb;
v_fb.push_back(makeFuture(true)); v_fb.push_back(makeFuture(true));
v_fb.push_back(makeFuture(false)); v_fb.push_back(makeFuture(false));
auto f = collectAll(v_fb); auto f = collectAll(v_fb).toUnsafeFuture();
f.wait(milliseconds(1)); f.wait(milliseconds(1));
EXPECT_TRUE(f.isReady()); EXPECT_TRUE(f.isReady());
EXPECT_EQ(2, f.value().size()); EXPECT_EQ(2, f.value().size());
...@@ -159,7 +159,7 @@ TEST(Wait, waitWithDuration) { ...@@ -159,7 +159,7 @@ TEST(Wait, waitWithDuration) {
Promise<bool> p2; Promise<bool> p2;
v_fb.push_back(p1.getFuture()); v_fb.push_back(p1.getFuture());
v_fb.push_back(p2.getFuture()); v_fb.push_back(p2.getFuture());
auto f = collectAll(v_fb); auto f = collectAll(v_fb).toUnsafeFuture();
f.wait(milliseconds(1)); f.wait(milliseconds(1));
EXPECT_FALSE(f.isReady()); EXPECT_FALSE(f.isReady());
p1.setValue(true); p1.setValue(true);
......
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