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

=delete Future::onError

Summary:
Replace Future::onError with Future::thenError:
 * to remove ambiguous typing
 * to ensure that the executor is not lost and the returned Future is still bound to an executor

This diff finally removes the legacy onError.

See:
https://fb.workplace.com/groups/fbcode/permalink/2002251863144976/
for details.

Reviewed By: yfeldblum, Orvid

Differential Revision: D15034702

fbshipit-source-id: 17faf37e79c0a31ad25c02542f09a8293e693899
parent e9440ced
......@@ -275,7 +275,8 @@ class CrappyExecutor : public Executor {
TEST(Executor, CrappyExecutor) {
CrappyExecutor x;
bool flag = false;
auto f = folly::via(&x).onError([&](std::runtime_error& e) {
auto f = folly::via(&x).thenError(
folly::tag_t<std::runtime_error>{}, [&](std::runtime_error&& e) {
EXPECT_STREQ("bad", e.what());
flag = true;
});
......
......@@ -128,7 +128,7 @@ class FutureDAG : public std::enable_shared_from_this<FutureDAG> {
nodes[handle].promise.setTry(std::move(t));
});
})
.onError([this, handle](exception_wrapper ew) {
.thenError([this, handle](exception_wrapper ew) {
nodes[handle].promise.setException(std::move(ew));
});
}
......
......@@ -1284,53 +1284,6 @@ Future<Unit> Future<T>::then() && {
return std::move(*this).thenValue([](T&&) {});
}
// onError where the callback returns T
template <class T>
template <class F>
typename std::enable_if<
!is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
typedef typename futures::detail::Extract<F>::FirstArg Exn;
static_assert(
std::is_same<typename futures::detail::Extract<F>::RawReturn, T>::value,
"Return type of onError callback must be T or Future<T>");
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(
tag_t<Exn>{},
[func = std::forward<F>(func)](auto&& ex) mutable {
return std::forward<F>(func)(ex);
})
.via(&InlineExecutor::instance());
}
// onError where the callback returns Future<T>
template <class T>
template <class F>
typename std::enable_if<
!is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
static_assert(
std::is_same<typename futures::detail::Extract<F>::Return, Future<T>>::
value,
"Return type of onError callback must be T or Future<T>");
typedef typename futures::detail::Extract<F>::FirstArg Exn;
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(
tag_t<Exn>{},
[func = std::forward<F>(func)](auto&& ex) mutable {
return std::forward<F>(func)(ex);
})
.via(&InlineExecutor::instance());
}
template <class T>
template <class F>
Future<T> Future<T>::ensure(F&& func) && {
......@@ -1351,43 +1304,6 @@ Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) && {
});
}
template <class T>
template <class F>
typename std::enable_if<
is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
static_assert(
std::is_same<typename futures::detail::Extract<F>::Return, Future<T>>::
value,
"Return type of onError callback must be T or Future<T>");
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(std::forward<F>(func))
.via(&InlineExecutor::instance());
}
// onError(exception_wrapper) that returns T
template <class T>
template <class F>
typename std::enable_if<
is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) && {
static_assert(
std::is_same<typename futures::detail::Extract<F>::Return, Future<T>>::
value,
"Return type of onError callback must be T or Future<T>");
// NOTE: Removes the executor to maintain historical behaviour.
return std::move(*this)
.thenError(std::forward<F>(func))
.via(&InlineExecutor::instance());
}
template <class Func>
auto via(Executor::KeepAlive<> x, Func&& func) -> Future<
typename isFutureOrSemiFuture<decltype(std::declval<Func>()())>::Inner> {
......
......@@ -1469,7 +1469,7 @@ class Future : private futures::detail::FutureBase<T> {
!is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
/// Overload of onError where the error continuation returns a Future<T>
///
......@@ -1489,7 +1489,7 @@ class Future : private futures::detail::FutureBase<T> {
!is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
/// Overload of onError that takes exception_wrapper and returns Future<T>
///
......@@ -1509,7 +1509,7 @@ class Future : private futures::detail::FutureBase<T> {
is_invocable<F, exception_wrapper>::value &&
futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
/// Overload of onError that takes exception_wrapper and returns T
///
......@@ -1529,16 +1529,15 @@ class Future : private futures::detail::FutureBase<T> {
is_invocable<F, exception_wrapper>::value &&
!futures::detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func) &&;
onError(F&& func) && = delete;
template <class R, class... Args>
Future<T> onError(R (&func)(Args...)) && {
return std::move(*this).onError(&func);
}
Future<T> onError(R (&func)(Args...)) && = delete;
// clang-format off
template <class F>
[[deprecated("ERROR: use rvalue-qualified fn, eg, std::move(future).onError(...)")]]
[[deprecated(
"onError loses the attached executor and is weakly typed. Please move to thenError instead.")]]
Future<T> onError(F&& func) & = delete;
/// func is like std::function<void()> and is executed unconditionally, and
......@@ -1559,7 +1558,7 @@ class Future : private futures::detail::FutureBase<T> {
Future<T> ensure(F&& func) &&;
// clang-format on
/// Like onError, but for timeouts. example:
/// Like thenError, but for timeouts. example:
///
/// Future<int> f = makeFuture<int>(42)
/// .delayed(long_time)
......
......@@ -26,7 +26,7 @@ using namespace folly;
namespace {
/***
* The basic premise is to check that the callback passed to then or onError
* The basic premise is to check that the callback passed to then or thenError
* is destructed before wait returns on the resulting future.
*
* The approach is to use callbacks where the destructor sleeps 500ms and then
......@@ -102,110 +102,123 @@ TEST_F(CallbackLifetimeTest, thenReturnsFutureThrows) {
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatch) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueMatch) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) {})
.thenError(folly::tag_t<std::exception>{}, [_ = mkCGuard(c)](auto&&) {})
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueMatchThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueMatchThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) { raise(); })
.thenError(
folly::tag_t<std::exception>{},
[_ = mkCGuard(c)](auto&&) { raise(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrong) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueWrong) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) {})
.thenError(folly::tag_t<std::logic_error>{}, [_ = mkCGuard(c)](auto&&) {})
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsValueWrongThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsValueWrongThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) { raise(); })
.thenError(
folly::tag_t<std::logic_error>{},
[_ = mkCGuard(c)](auto&&) { raise(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatch) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureMatch) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) { return makeFuture(); })
.thenError(
folly::tag_t<std::exception>{},
[_ = mkCGuard(c)](auto&&) { return makeFuture(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureMatchThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureMatchThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::exception&) { return raiseFut(); })
.thenError(
folly::tag_t<std::exception>{},
[_ = mkCGuard(c)](auto&&) { return raiseFut(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrong) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureWrong) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) { return makeFuture(); })
.thenError(
folly::tag_t<std::logic_error>{},
[_ = mkCGuard(c)](auto&&) { return makeFuture(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesExnReturnsFutureWrongThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesExnReturnsFutureWrongThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](std::logic_error&) { return raiseFut(); })
.thenError(
folly::tag_t<std::logic_error>{},
[_ = mkCGuard(c)](auto&&) { return raiseFut(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValue) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsValue) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) {})
.thenError([_ = mkCGuard(c)](exception_wrapper&&) {})
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsValueThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsValueThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) { raise(); })
.thenError([_ = mkCGuard(c)](exception_wrapper&&) { raise(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFuture) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsFuture) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) { return makeFuture(); })
.thenError(
[_ = mkCGuard(c)](exception_wrapper&&) { return makeFuture(); })
.wait();
EXPECT_EQ(1, *c);
}
TEST_F(CallbackLifetimeTest, onErrorTakesWrapReturnsFutureThrows) {
TEST_F(CallbackLifetimeTest, thenErrorTakesWrapReturnsFutureThrows) {
auto c = mkC();
via(&executor)
.thenValue(raise)
.onError([_ = mkCGuard(c)](exception_wrapper&&) { return raiseFut(); })
.thenError([_ = mkCGuard(c)](exception_wrapper&&) { return raiseFut(); })
.wait();
EXPECT_EQ(1, *c);
}
......@@ -314,15 +314,6 @@ TEST(Future, hasPostconditionInvalid) {
}
namespace {
Future<int> onErrorHelperEggs(eggs_t&) {
return makeFuture(10);
}
Future<int> onErrorHelperGeneric(std::exception&) {
return makeFuture(20);
}
Future<int> onErrorHelperWrapper(folly::exception_wrapper&) {
return makeFuture(30);
}
Future<int> thenErrorHelperEggs(eggs_t&&) {
return makeFuture(10);
}
......@@ -334,7 +325,7 @@ Future<int> thenErrorHelperWrapper(folly::exception_wrapper&&) {
}
} // namespace
TEST(Future, onError) {
TEST(Future, thenError) {
bool theFlag = false;
auto flag = [&] { theFlag = true; };
#define EXPECT_FLAG() \
......@@ -351,9 +342,10 @@ TEST(Future, onError) {
// By reference
{
auto f = makeFuture()
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) { flag(); });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -361,7 +353,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
flag();
return makeFuture();
});
......@@ -371,9 +363,10 @@ TEST(Future, onError) {
// By value
{
auto f = makeFuture()
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) { flag(); });
.thenError(folly::tag_t<eggs_t>{}, [&](auto /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -381,7 +374,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto /* e */) {
flag();
return makeFuture();
});
......@@ -393,15 +386,18 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) { flag(); });
.thenError(
folly::tag_t<std::exception>{},
[&](auto&& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) {
.thenError(folly::tag_t<std::exception>{}, [&](auto&& /* e */) {
flag();
return makeFuture();
});
......@@ -413,7 +409,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) { flag(); });
.thenError(folly::tag_t<int>{}, [&](int /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
......@@ -421,7 +417,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) {
.thenError(folly::tag_t<int>{}, [&](int /* e */) {
flag();
return makeFuture();
});
......@@ -433,15 +429,18 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable { flag(); });
.thenError(
folly::tag_t<eggs_t>{},
[&](auto&& /* e */) mutable { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) mutable {
flag();
return makeFuture();
});
......@@ -451,25 +450,26 @@ TEST(Future, onError) {
// Function pointer
{
auto f = makeFuture()
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
.thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs)
.thenError(folly::tag_t<std::exception>{}, thenErrorHelperGeneric);
EXPECT_EQ(10, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
.thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs)
.thenError(folly::tag_t<std::exception>{}, thenErrorHelperGeneric);
EXPECT_EQ(20, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs);
.thenError(folly::tag_t<eggs_t>{}, thenErrorHelperEggs);
EXPECT_THROW(f.value(), std::runtime_error);
}
{
......@@ -499,7 +499,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
flag();
return -1;
});
......@@ -510,7 +510,7 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
flag();
return makeFuture<int>(-1);
});
......@@ -522,302 +522,18 @@ TEST(Future, onError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) { flag(); });
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) {
flag();
return makeFuture();
});
.thenError(
folly::tag_t<std::runtime_error>{},
[&](auto&& /* e */) { flag(); });
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// Returned value propagates
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return 42; });
EXPECT_EQ(42, f.value());
}
// Returned future propagates
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return makeFuture<int>(42); });
EXPECT_EQ(42, f.value());
}
// Throw in callback
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> int { throw e; });
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> Future<int> { throw e; });
EXPECT_THROW(f.value(), eggs_t);
}
// exception_wrapper, return Future<T>
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](exception_wrapper /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// exception_wrapper, return Future<T> but throw
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> Future<int> {
flag();
throw eggs;
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// exception_wrapper, return T
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) {
flag();
return -1;
});
EXPECT_FLAG();
EXPECT_EQ(-1, f.value());
}
// exception_wrapper, return T but throw
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> int {
flag();
throw eggs;
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// const exception_wrapper&
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](const exception_wrapper& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
#undef EXPECT_FLAG
#undef EXPECT_NO_FLAG
}
TEST(Future, thenError) {
bool theFlag = false;
auto flag = [&] { theFlag = true; };
#define EXPECT_FLAG() \
do { \
EXPECT_TRUE(theFlag); \
theFlag = false; \
} while (0);
#define EXPECT_NO_FLAG() \
do { \
EXPECT_FALSE(theFlag); \
theFlag = false; \
} while (0);
// By reference
{
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(tag_t<eggs_t>{}, [&](const eggs_t& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// By auto reference
{
auto f =
makeFuture()
.thenValue([](auto&&) { throw eggs; })
.thenError(tag_t<eggs_t>{}, [&](auto const& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// By value
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Polymorphic
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::exception& /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Non-exceptions
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw - 1; })
.onError([&](int /* e */) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Mutable lambda
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable { flag(); });
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](eggs_t& /* e */) mutable {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
// Function pointer
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
EXPECT_EQ(10, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs)
.onError(onErrorHelperGeneric);
EXPECT_EQ(20, f.value());
}
{
auto f =
makeFuture()
.thenValue([](auto &&) -> int { throw std::runtime_error("test"); })
.onError(onErrorHelperEggs);
EXPECT_THROW(f.value(), std::runtime_error);
}
// No throw
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
flag();
return -1;
});
EXPECT_NO_FLAG();
EXPECT_EQ(42, f.value());
}
{
auto f = makeFuture()
.thenValue([](auto&&) { return 42; })
.onError([&](eggs_t& /* e */) {
flag();
return makeFuture<int>(-1);
});
EXPECT_NO_FLAG();
EXPECT_EQ(42, f.value());
}
// Catch different exception
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) { flag(); });
EXPECT_NO_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](std::runtime_error& /* e */) {
.thenError(folly::tag_t<std::runtime_error>{}, [&](auto&& /* e */) {
flag();
return makeFuture();
});
......@@ -829,7 +545,9 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return 42; });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
return 42;
});
EXPECT_EQ(42, f.value());
}
......@@ -837,7 +555,9 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& /* e */) { return makeFuture<int>(42); });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& /* e */) {
return makeFuture<int>(42);
});
EXPECT_EQ(42, f.value());
}
......@@ -845,14 +565,18 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> int { throw e; });
.thenError(folly::tag_t<eggs_t>{}, [&](auto&& e) -> int {
throw std::move(e);
});
EXPECT_THROW(f.value(), eggs_t);
}
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](eggs_t& e) -> Future<int> { throw e; });
.thenError(
folly::tag_t<eggs_t>{},
[&](auto&& e) -> Future<int> { throw std::move(e); });
EXPECT_THROW(f.value(), eggs_t);
}
......@@ -860,7 +584,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](exception_wrapper /* e */) {
.thenError([&](exception_wrapper /* e */) {
flag();
return makeFuture();
});
......@@ -872,7 +596,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> Future<int> {
.thenError([&](exception_wrapper /* e */) -> Future<int> {
flag();
throw eggs;
});
......@@ -884,7 +608,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) {
.thenError([&](exception_wrapper /* e */) {
flag();
return -1;
});
......@@ -896,7 +620,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto &&) -> int { throw eggs; })
.onError([&](exception_wrapper /* e */) -> int {
.thenError([&](exception_wrapper /* e */) -> int {
flag();
throw eggs;
});
......@@ -908,7 +632,7 @@ TEST(Future, thenError) {
{
auto f = makeFuture()
.thenValue([](auto&&) { throw eggs; })
.onError([&](const exception_wrapper& /* e */) {
.thenError([&](const exception_wrapper& /* e */) {
flag();
return makeFuture();
});
......
......@@ -52,8 +52,8 @@ class ThrowingExecutor : public folly::Executor {
TEST(SelfDestruct, throwingExecutor) {
ThrowingExecutor executor;
auto* p = new Promise<int>();
auto future =
p->getFuture().via(&executor).onError([p](ThrowingExecutorError const&) {
auto future = p->getFuture().via(&executor).thenError(
folly::tag_t<ThrowingExecutorError>{}, [p](auto const&) {
delete p;
return 456;
});
......@@ -71,7 +71,9 @@ TEST(SelfDestruct, throwingInlineExecutor) {
delete p;
throw ThrowingExecutorError("callback throws");
})
.onError([](ThrowingExecutorError const&) { return 456; });
.thenError(
folly::tag_t<ThrowingExecutorError>{},
[](auto const&) { return 456; });
p->setValue(123);
EXPECT_EQ(456, std::move(future).get());
}
......@@ -68,7 +68,9 @@ TEST(Times, success) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -94,7 +96,9 @@ TEST(Times, failure) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -122,7 +126,9 @@ TEST(Times, interrupt) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::times(3, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
EXPECT_EQ(0, interrupt);
......
......@@ -70,7 +70,9 @@ TEST(WhileDo, success) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::whileDo(pred, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -98,7 +100,9 @@ TEST(WhileDo, failure) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::whileDo(pred, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
popAndFulfillPromise(ps, ps_mutex);
EXPECT_FALSE(complete);
......@@ -128,7 +132,9 @@ TEST(WhileDo, interrupt) {
auto thunk = makeThunk(ps, interrupt, ps_mutex);
auto f = folly::whileDo(pred, thunk)
.thenValue([&](auto&&) mutable { complete = true; })
.onError([&](FutureException& /* e */) { failure = true; });
.thenError(folly::tag_t<FutureException>{}, [&](auto&& /* e */) {
failure = true;
});
EXPECT_EQ(0, interrupt);
......
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