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

Add SemiFuture support to folly::times

Summary: times is hard to use with SemiFutures, leading to potential use of inline executors just to make it work. This adds native support.

Reviewed By: yfeldblum

Differential Revision: D16992774

fbshipit-source-id: 483110d923ca51e037439ccc80173ffa9299cd64
parent 4a150ab2
...@@ -2478,7 +2478,7 @@ whileDo(P&& predicate, F&& thunk) { ...@@ -2478,7 +2478,7 @@ whileDo(P&& predicate, F&& thunk) {
} }
template <class F> template <class F>
Future<Unit> times(const int n, F&& thunk) { auto times(const int n, F&& thunk) {
return folly::whileDo( return folly::whileDo(
[n, count = std::make_unique<std::atomic<int>>(0)]() mutable { [n, count = std::make_unique<std::atomic<int>>(0)]() mutable {
return count->fetch_add(1, std::memory_order_relaxed) < n; return count->fetch_add(1, std::memory_order_relaxed) < n;
......
...@@ -1858,12 +1858,6 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1858,12 +1858,6 @@ class Future : private futures::detail::FutureBase<T> {
template <class T2> template <class T2>
friend Future<T2> makeFuture(Try<T2>); friend Future<T2> makeFuture(Try<T2>);
/// Repeat the given future (i.e., the computation it contains) n times.
///
/// thunk behaves like std::function<Future<T2>(void)>
template <class F>
friend Future<Unit> times(int n, F&& thunk);
/// Carry out the computation contained in the given future if /// Carry out the computation contained in the given future if
/// the predicate holds. /// the predicate holds.
/// ///
...@@ -2495,6 +2489,15 @@ template <class P, class F> ...@@ -2495,6 +2489,15 @@ template <class P, class F>
typename std:: typename std::
enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type
whileDo(P&& predicate, F&& thunk); whileDo(P&& predicate, F&& thunk);
/// Repeat the given future (i.e., the computation it contains) n times.
///
/// thunk behaves like
/// std::function<Future<T2>(void)>
/// or
/// std::function<SemiFuture<T2>(void)>
template <class F>
auto times(int n, F&& thunk);
} // namespace folly } // namespace folly
#if FOLLY_HAS_COROUTINES #if FOLLY_HAS_COROUTINES
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <folly/executors/ManualExecutor.h>
#include <folly/futures/Future.h> #include <folly/futures/Future.h>
#include <folly/futures/Promise.h> #include <folly/futures/Promise.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
...@@ -58,49 +59,79 @@ inline std::function<bool(void)> makePred(int& i) { ...@@ -58,49 +59,79 @@ inline std::function<bool(void)> makePred(int& i) {
}; };
} }
TEST(Times, success) { template <class F>
std::queue<std::shared_ptr<Promise<Unit>>> ps; inline void successTest(
std::mutex ps_mutex; std::queue<std::shared_ptr<Promise<Unit>>>& ps,
int interrupt = 0; std::mutex& ps_mutex,
F& thunk) {
folly::ManualExecutor executor;
bool complete = false; bool complete = false;
bool failure = false; bool failure = false;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk) auto f = folly::times(3, thunk)
.via(&executor)
.thenValue([&](auto&&) mutable { complete = true; }) .thenValue([&](auto&&) mutable { complete = true; })
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) { .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true; failure = true;
}); });
executor.drain();
popAndFulfillPromise(ps, ps_mutex); popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete); EXPECT_FALSE(complete);
EXPECT_FALSE(failure); EXPECT_FALSE(failure);
executor.drain();
popAndFulfillPromise(ps, ps_mutex); popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete); EXPECT_FALSE(complete);
EXPECT_FALSE(failure); EXPECT_FALSE(failure);
executor.drain();
popAndFulfillPromise(ps, ps_mutex); popAndFulfillPromise(ps, ps_mutex);
executor.drain();
EXPECT_TRUE(f.isReady()); EXPECT_TRUE(f.isReady());
EXPECT_TRUE(complete); EXPECT_TRUE(complete);
EXPECT_FALSE(failure); EXPECT_FALSE(failure);
} }
TEST(Times, failure) { TEST(Times, success) {
std::queue<std::shared_ptr<Promise<Unit>>> ps; std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex; std::mutex ps_mutex;
int interrupt = 0; int interrupt = 0;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
successTest(ps, ps_mutex, thunk);
}
TEST(Times, semiFutureSuccess) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
int interrupt = 0;
auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {
return t().semi();
};
successTest(ps, ps_mutex, thunk);
}
template <class F>
inline void failureTest(
std::queue<std::shared_ptr<Promise<Unit>>>& ps,
std::mutex& ps_mutex,
F& thunk) {
folly::ManualExecutor executor;
bool complete = false; bool complete = false;
bool failure = false; bool failure = false;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk) auto f = folly::times(3, thunk)
.via(&executor)
.thenValue([&](auto&&) mutable { complete = true; }) .thenValue([&](auto&&) mutable { complete = true; })
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) { .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true; failure = true;
}); });
executor.drain();
popAndFulfillPromise(ps, ps_mutex); popAndFulfillPromise(ps, ps_mutex);
executor.drain();
EXPECT_FALSE(complete); EXPECT_FALSE(complete);
EXPECT_FALSE(failure); EXPECT_FALSE(failure);
...@@ -111,32 +142,76 @@ TEST(Times, failure) { ...@@ -111,32 +142,76 @@ TEST(Times, failure) {
FutureException eggs("eggs"); FutureException eggs("eggs");
p2->setException(eggs); p2->setException(eggs);
executor.drain();
EXPECT_TRUE(f.isReady()); EXPECT_TRUE(f.isReady());
EXPECT_FALSE(complete); EXPECT_FALSE(complete);
EXPECT_TRUE(failure); EXPECT_TRUE(failure);
} }
TEST(Times, interrupt) { TEST(Times, failure) {
std::queue<std::shared_ptr<Promise<Unit>>> ps; std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex; std::mutex ps_mutex;
int interrupt = 0; int interrupt = 0;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
failureTest(ps, ps_mutex, thunk);
}
TEST(Times, semiFutureFailure) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
int interrupt = 0;
auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {
return t().semi();
};
failureTest(ps, ps_mutex, thunk);
}
template <class F>
inline void interruptTest(
std::queue<std::shared_ptr<Promise<Unit>>>& ps,
std::mutex& ps_mutex,
int& interrupt,
F& thunk) {
folly::ManualExecutor executor;
bool complete = false; bool complete = false;
bool failure = false; bool failure = false;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk) auto f = folly::times(3, thunk)
.via(&executor)
.thenValue([&](auto&&) mutable { complete = true; }) .thenValue([&](auto&&) mutable { complete = true; })
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) { .thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true; failure = true;
}); });
executor.drain();
EXPECT_EQ(0, interrupt); EXPECT_EQ(0, interrupt);
FutureException eggs("eggs"); FutureException eggs("eggs");
f.raise(eggs); f.raise(eggs);
executor.drain();
for (int i = 1; i <= 3; ++i) { for (int i = 1; i <= 3; ++i) {
EXPECT_EQ(1, interrupt); EXPECT_EQ(1, interrupt);
popAndFulfillPromise(ps, ps_mutex); popAndFulfillPromise(ps, ps_mutex);
executor.drain();
} }
} }
TEST(Times, interrupt) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
int interrupt = 0;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
interruptTest(ps, ps_mutex, interrupt, thunk);
}
TEST(Times, semiFutureInterrupt) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
int interrupt = 0;
auto thunk = [t = makeThunk(ps, interrupt, ps_mutex)]() {
return t().semi();
};
interruptTest(ps, ps_mutex, interrupt, thunk);
}
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