Commit d3ceb232 authored by James Sedgwick's avatar James Sedgwick Committed by Alecs King

onError(exception_wrapper)

Summary: title

Test Plan: unit

Reviewed By: hans@fb.com

Subscribers: folly-diffs@, jsedgwick, yfeldblum, chalfant, hannesr, vloh

FB internal diff: D1984864

Tasks: 6045789

Signature: t1:1984864:1429116418:b4a9cdbb88f605a09b5753eea41dd970c96b9d4e
parent 0e46cb95
...@@ -244,6 +244,7 @@ Future<void> Future<T>::then() { ...@@ -244,6 +244,7 @@ Future<void> Future<T>::then() {
template <class T> template <class T>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!detail::callableWith<F, exception_wrapper>::value &&
!detail::Extract<F>::ReturnsFuture::value, !detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type Future<T>>::type
Future<T>::onError(F&& func) { Future<T>::onError(F&& func) {
...@@ -273,6 +274,7 @@ Future<T>::onError(F&& func) { ...@@ -273,6 +274,7 @@ Future<T>::onError(F&& func) {
template <class T> template <class T>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!detail::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value, detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type Future<T>>::type
Future<T>::onError(F&& func) { Future<T>::onError(F&& func) {
...@@ -323,6 +325,70 @@ Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) { ...@@ -323,6 +325,70 @@ Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) {
.onError([funcw](TimedOut const&) { return (*funcw)(); }); .onError([funcw](TimedOut const&) { return (*funcw)(); });
} }
template <class T>
template <class F>
typename std::enable_if<
detail::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) {
static_assert(
std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
"Return type of onError callback must be T or Future<T>");
Promise<T> p;
auto f = p.getFuture();
auto pm = folly::makeMoveWrapper(std::move(p));
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T> t) mutable {
if (t.hasException()) {
try {
auto f2 = (*funcm)(std::move(t.exception()));
f2.setCallback_([pm](Try<T> t2) mutable {
pm->fulfilTry(std::move(t2));
});
} catch (const std::exception& e2) {
pm->setException(exception_wrapper(std::current_exception(), e2));
} catch (...) {
pm->setException(exception_wrapper(std::current_exception()));
}
} else {
pm->fulfilTry(std::move(t));
}
});
return f;
}
// onError(exception_wrapper) that returns T
template <class T>
template <class F>
typename std::enable_if<
detail::callableWith<F, exception_wrapper>::value &&
!detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
Future<T>::onError(F&& func) {
static_assert(
std::is_same<typename detail::Extract<F>::Return, Future<T>>::value,
"Return type of onError callback must be T or Future<T>");
Promise<T> p;
auto f = p.getFuture();
auto pm = folly::makeMoveWrapper(std::move(p));
auto funcm = folly::makeMoveWrapper(std::move(func));
setCallback_([pm, funcm](Try<T> t) mutable {
if (t.hasException()) {
pm->fulfil([&]{
return (*funcm)(std::move(t.exception()));
});
} else {
pm->fulfilTry(std::move(t));
}
});
return f;
}
template <class T> template <class T>
typename std::add_lvalue_reference<T>::type Future<T>::value() { typename std::add_lvalue_reference<T>::type Future<T>::value() {
throwIfInvalid(); throwIfInvalid();
......
...@@ -344,6 +344,7 @@ class Future { ...@@ -344,6 +344,7 @@ class Future {
/// }); /// });
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!detail::callableWith<F, exception_wrapper>::value &&
!detail::Extract<F>::ReturnsFuture::value, !detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type Future<T>>::type
onError(F&& func); onError(F&& func);
...@@ -351,10 +352,27 @@ class Future { ...@@ -351,10 +352,27 @@ class Future {
/// Overload of onError where the error callback returns a Future<T> /// Overload of onError where the error callback returns a Future<T>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!detail::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value, detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type Future<T>>::type
onError(F&& func); onError(F&& func);
/// Overload of onError that takes exception_wrapper and returns Future<T>
template <class F>
typename std::enable_if<
detail::callableWith<F, exception_wrapper>::value &&
detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func);
/// Overload of onError that takes exception_wrapper and returns T
template <class F>
typename std::enable_if<
detail::callableWith<F, exception_wrapper>::value &&
!detail::Extract<F>::ReturnsFuture::value,
Future<T>>::type
onError(F&& func);
/// func is like std::function<void()> and is executed unconditionally, and /// func is like std::function<void()> and is executed unconditionally, and
/// the value/exception is passed through to the resulting Future. /// the value/exception is passed through to the resulting Future.
/// func shouldn't throw, but if it does it will be captured and propagated, /// func shouldn't throw, but if it does it will be captured and propagated,
......
...@@ -260,6 +260,66 @@ TEST(Future, onError) { ...@@ -260,6 +260,66 @@ TEST(Future, onError) {
.onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); }); .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
EXPECT_THROW(f.value(), eggs_t); EXPECT_THROW(f.value(), eggs_t);
} }
// exception_wrapper, return Future<T>
{
auto f = makeFuture()
.then([] { 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()
.then([]{ throw eggs; return 0; })
.onError([&] (exception_wrapper e) {
flag();
throw eggs;
return makeFuture<int>(-1);
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// exception_wrapper, return T
{
auto f = makeFuture()
.then([]{ throw eggs; return 0; })
.onError([&] (exception_wrapper e) {
flag();
return -1;
});
EXPECT_FLAG();
EXPECT_EQ(-1, f.value());
}
// exception_wrapper, return T but throw
{
auto f = makeFuture()
.then([]{ throw eggs; return 0; })
.onError([&] (exception_wrapper e) {
flag();
throw eggs;
return -1;
});
EXPECT_FLAG();
EXPECT_THROW(f.value(), eggs_t);
}
// const exception_wrapper&
{
auto f = makeFuture()
.then([] { throw eggs; })
.onError([&] (const exception_wrapper& e) {
flag();
return makeFuture();
});
EXPECT_FLAG();
EXPECT_NO_THROW(f.value());
}
} }
TEST(Future, try) { TEST(Future, try) {
......
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