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

Remove thenError dependence on onError

Summary:
* Split thenError into future-returning and not-future-returning forms as we cannot rely on onError for this.
 * Move core functionality from onError into thenError to avoid thenError depending on onError.
 * Makes thenError's continuation consistent with other forms such that the parameter is passed by r-value, hence an l-value reference is no longer valid.

Reviewed By: yfeldblum

Differential Revision: D13820171

fbshipit-source-id: cf42a7d1c0759c5cfbb03f8e66a6d6988f7e10c7
parent bf084642
...@@ -1111,41 +1111,116 @@ Future<T>::thenValue(F&& func) && { ...@@ -1111,41 +1111,116 @@ Future<T>::thenValue(F&& func) && {
template <class T> template <class T>
template <class ExceptionType, class F> template <class ExceptionType, class F>
Future<T> Future<T>::thenError(F&& func) && { typename std::enable_if<
// Forward to onError but ensure that returned future carries the executor isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
// Allow for applying to future with null executor while this is still Future<T>>::type
// possible. Future<T>::thenError(F&& func) && {
Promise<T> p;
auto sf = p.getSemiFuture();
auto* ePtr = this->getExecutor(); auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
FOLLY_PUSH_WARNING this->setCallback_(
FOLLY_GNU_DISABLE_WARNING("-Wdeprecated-declarations") [state = futures::detail::makeCoreCallbackState(
return std::move(*this) std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
.onError([func = std::forward<F>(func)](ExceptionType& ex) mutable { if (auto ex = t.template tryGetExceptionObject<
return std::forward<F>(func)(ex); std::remove_reference_t<ExceptionType>>()) {
}) auto tf2 = state.tryInvoke(*ex);
.via(std::move(e)); if (tf2.hasException()) {
FOLLY_POP_WARNING state.setException(std::move(tf2.exception()));
} else {
tf2->setCallback_([p = state.stealPromise()](Try<T>&& t3) mutable {
p.setTry(std::move(t3));
});
}
} else {
state.setTry(std::move(t));
}
});
return std::move(sf).via(std::move(e));
}
template <class T>
template <class ExceptionType, class F>
typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type
Future<T>::thenError(F&& func) && {
Promise<T> p;
p.core_->setInterruptHandlerNoLock(this->getCore().getInterruptHandler());
auto sf = p.getSemiFuture();
auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
this->setCallback_([state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](
Try<T>&& t) mutable {
if (auto ex = t.template tryGetExceptionObject<
std::remove_reference_t<ExceptionType>>()) {
state.setTry(makeTryWith([&] { return state.invoke(std::move(*ex)); }));
} else {
state.setTry(std::move(t));
}
});
return std::move(sf).via(std::move(e));
} }
template <class T> template <class T>
template <class F> template <class F>
Future<T> Future<T>::thenError(F&& func) && { typename std::enable_if<
// Forward to onError but ensure that returned future carries the executor isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
// Allow for applying to future with null executor while this is still Future<T>>::type
// possible. Future<T>::thenError(F&& func) && {
auto* ePtr = this->getExecutor(); auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance()); auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
FOLLY_PUSH_WARNING Promise<T> p;
FOLLY_GNU_DISABLE_WARNING("-Wdeprecated-declarations") auto sf = p.getSemiFuture();
return std::move(*this) this->setCallback_(
.onError([func = std::forward<F>(func)]( [state = futures::detail::makeCoreCallbackState(
folly::exception_wrapper&& ex) mutable { std::move(p), std::forward<F>(func))](Try<T> t) mutable {
return std::forward<F>(func)(std::move(ex)); if (t.hasException()) {
}) auto tf2 = state.tryInvoke(std::move(t.exception()));
.via(std::move(e)); if (tf2.hasException()) {
FOLLY_POP_WARNING state.setException(std::move(tf2.exception()));
} else {
tf2->setCallback_([p = state.stealPromise()](Try<T>&& t3) mutable {
p.setTry(std::move(t3));
});
}
} else {
state.setTry(std::move(t));
}
});
return std::move(sf).via(std::move(e));
}
template <class T>
template <class F>
typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
Future<T>>::type
Future<T>::thenError(F&& func) && {
auto* ePtr = this->getExecutor();
auto e = folly::getKeepAliveToken(ePtr ? *ePtr : InlineExecutor::instance());
Promise<T> p;
auto sf = p.getSemiFuture();
this->setCallback_(
[state = futures::detail::makeCoreCallbackState(
std::move(p), std::forward<F>(func))](Try<T>&& t) mutable {
if (t.hasException()) {
state.setTry(makeTryWith(
[&] { return state.invoke(std::move(t.exception())); }));
} else {
state.setTry(std::move(t));
}
});
return std::move(sf).via(std::move(e));
} }
template <class T> template <class T>
......
...@@ -1323,7 +1323,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1323,7 +1323,16 @@ class Future : private futures::detail::FutureBase<T> {
/// - `valid() == false` /// - `valid() == false`
/// - `RESULT.valid() == true` /// - `RESULT.valid() == true`
template <class ExceptionType, class F> template <class ExceptionType, class F>
Future<T> thenError(F&& func) &&; typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type
thenError(F&& func) &&;
template <class ExceptionType, class F>
typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, ExceptionType>>::value,
Future<T>>::type
thenError(F&& func) &&;
template <class ExceptionType, class R, class... Args> template <class ExceptionType, class R, class... Args>
Future<T> thenError(R (&func)(Args...)) && { Future<T> thenError(R (&func)(Args...)) && {
...@@ -1355,7 +1364,16 @@ class Future : private futures::detail::FutureBase<T> { ...@@ -1355,7 +1364,16 @@ class Future : private futures::detail::FutureBase<T> {
/// - `valid() == false` /// - `valid() == false`
/// - `RESULT.valid() == true` /// - `RESULT.valid() == true`
template <class F> template <class F>
Future<T> thenError(F&& func) &&; typename std::enable_if<
isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
Future<T>>::type
thenError(F&& func) &&;
template <class F>
typename std::enable_if<
!isFutureOrSemiFuture<invoke_result_t<F, exception_wrapper>>::value,
Future<T>>::type
thenError(F&& func) &&;
template <class R, class... Args> template <class R, class... Args>
Future<T> thenError(R (&func)(Args...)) && { Future<T> thenError(R (&func)(Args...)) && {
......
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