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) {
}
template <class F>
Future<Unit> times(const int n, F&& thunk) {
auto times(const int n, F&& thunk) {
return folly::whileDo(
[n, count = std::make_unique<std::atomic<int>>(0)]() mutable {
return count->fetch_add(1, std::memory_order_relaxed) < n;
......
......@@ -1858,12 +1858,6 @@ class Future : private futures::detail::FutureBase<T> {
template <class 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
/// the predicate holds.
///
......@@ -2495,6 +2489,15 @@ template <class P, class F>
typename std::
enable_if<isSemiFuture<invoke_result_t<F>>::value, SemiFuture<Unit>>::type
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
#if FOLLY_HAS_COROUTINES
......
......@@ -18,6 +18,7 @@
#include <mutex>
#include <queue>
#include <folly/executors/ManualExecutor.h>
#include <folly/futures/Future.h>
#include <folly/futures/Promise.h>
#include <folly/portability/GTest.h>
......@@ -58,49 +59,79 @@ inline std::function<bool(void)> makePred(int& i) {
};
}
TEST(Times, success) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
int interrupt = 0;
template <class F>
inline void successTest(
std::queue<std::shared_ptr<Promise<Unit>>>& ps,
std::mutex& ps_mutex,
F& thunk) {
folly::ManualExecutor executor;
bool complete = false;
bool failure = false;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.via(&executor)
.thenValue([&](auto&&) mutable { complete = true; })
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
executor.drain();
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
EXPECT_FALSE(failure);
executor.drain();
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
EXPECT_FALSE(failure);
executor.drain();
popAndFulfillPromise(ps, ps_mutex);
executor.drain();
EXPECT_TRUE(f.isReady());
EXPECT_TRUE(complete);
EXPECT_FALSE(failure);
}
TEST(Times, failure) {
TEST(Times, success) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
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 failure = false;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.via(&executor)
.thenValue([&](auto&&) mutable { complete = true; })
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
executor.drain();
popAndFulfillPromise(ps, ps_mutex);
executor.drain();
EXPECT_FALSE(complete);
EXPECT_FALSE(failure);
......@@ -111,32 +142,76 @@ TEST(Times, failure) {
FutureException eggs("eggs");
p2->setException(eggs);
executor.drain();
EXPECT_TRUE(f.isReady());
EXPECT_FALSE(complete);
EXPECT_TRUE(failure);
}
TEST(Times, interrupt) {
TEST(Times, failure) {
std::queue<std::shared_ptr<Promise<Unit>>> ps;
std::mutex ps_mutex;
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 failure = false;
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.via(&executor)
.thenValue([&](auto&&) mutable { complete = true; })
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
executor.drain();
EXPECT_EQ(0, interrupt);
FutureException eggs("eggs");
f.raise(eggs);
executor.drain();
for (int i = 1; i <= 3; ++i) {
EXPECT_EQ(1, interrupt);
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