summaryrefslogtreecommitdiff
path: root/src/mongo/util
diff options
context:
space:
mode:
authorMatt Diener <matt.diener@mongodb.com>2022-05-26 16:31:24 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-05-26 17:49:43 +0000
commit4aad7bee6c9605480c55cd6fe274fc693654b7bc (patch)
tree4f436e2c93e81f5f7a3430137b45f4867cb434af /src/mongo/util
parenta5dd1d90a957e17f954b2df97d3da4a3644e166f (diff)
downloadmongo-4aad7bee6c9605480c55cd6fe274fc693654b7bc.tar.gz
SERVER-66128 Add Policy to Future Func-using API
Diffstat (limited to 'src/mongo/util')
-rw-r--r--src/mongo/util/future.h239
-rw-r--r--src/mongo/util/future_impl.h110
2 files changed, 256 insertions, 93 deletions
diff --git a/src/mongo/util/future.h b/src/mongo/util/future.h
index cfdb724a101..57e560f0cf2 100644
--- a/src/mongo/util/future.h
+++ b/src/mongo/util/future.h
@@ -369,10 +369,11 @@ public:
*
* TODO decide how to handle func throwing.
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>)
- void getAsync(Func&& func) && noexcept {
- std::move(this->_impl).getAsync(std::forward<Func>(func));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>&&
+ isFuturePolicy<Policy>)
+ void getAsync(Policy policy, Func&& func) && noexcept {
+ std::move(this->_impl).getAsync(policy, std::forward<Func>(func));
}
//
@@ -410,10 +411,10 @@ public:
* The callback takes a T and can return anything (see above for how Statusy and Futurey returns
* are handled.)
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallable<Func, T>)
- /*see above*/ auto then(Func&& func) && noexcept {
- return wrap<Func, T>(std::move(this->_impl).then(std::forward<Func>(func)));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallable<Func, T>&& isFuturePolicy<Policy>)
+ /*see above*/ auto then(Policy policy, Func&& func) && noexcept {
+ return wrap<Func, T>(std::move(this->_impl).then(policy, std::forward<Func>(func)));
}
/**
@@ -423,10 +424,11 @@ public:
* The callback takes a StatusOrStatusWith<T> and can return anything (see above for how Statusy
* and Futurey returns are handled.)
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>)
- /*see above*/ auto onCompletion(Func&& func) && noexcept {
- return wrap<Func, Status>(std::move(this->_impl).onCompletion(std::forward<Func>(func)));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>&& isFuturePolicy<Policy>)
+ /*see above*/ auto onCompletion(Policy policy, Func&& func) && noexcept {
+ return wrap<Func, Status>(
+ std::move(this->_impl).onCompletion(policy, std::forward<Func>(func)));
}
/**
@@ -442,10 +444,10 @@ public:
* The callback takes a non-OK Status and returns a possibly-wrapped T (see above for how
* Statusy and Futurey returns are handled.)
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableR<T, Func, Status>)
- /*see above*/ auto onError(Func&& func) && noexcept {
- return wrap<Func, Status>(std::move(this->_impl).onError(std::forward<Func>(func)));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>&& isFuturePolicy<Policy>)
+ /*see above*/ auto onError(Policy policy, Func&& func) && noexcept {
+ return wrap<Func, Status>(std::move(this->_impl).onError(policy, std::forward<Func>(func)));
}
/**
@@ -455,11 +457,11 @@ public:
* The callback takes a non-OK Status and returns a possibly-wrapped T (see above for how
* Statusy and Futurey returns are handled.)
*/
- TEMPLATE(ErrorCodes::Error code, typename Func)
- REQUIRES(future_details::isCallableR<T, Func, Status>)
- /*see above*/ auto onError(Func&& func) && noexcept {
+ TEMPLATE(ErrorCodes::Error code, typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>&& isFuturePolicy<Policy>)
+ /*see above*/ auto onError(Policy policy, Func&& func) && noexcept {
return wrap<Func, Status>(
- std::move(this->_impl).template onError<code>(std::forward<Func>(func)));
+ std::move(this->_impl).template onError<code>(policy, std::forward<Func>(func)));
}
/**
@@ -469,11 +471,12 @@ public:
* The callback takes a non-OK Status and returns a possibly-wrapped T (see above for how
* Statusy and Futurey returns are handled.)
*/
- TEMPLATE(ErrorCategory category, typename Func)
- REQUIRES(future_details::isCallableR<T, Func, Status>)
- /*see above*/ auto onErrorCategory(Func&& func) && noexcept {
+ TEMPLATE(ErrorCategory category, typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>&& isFuturePolicy<Policy>)
+ /*see above*/ auto onErrorCategory(Policy policy, Func&& func) && noexcept {
return wrap<Func, Status>(
- std::move(this->_impl).template onErrorCategory<category>(std::forward<Func>(func)));
+ std::move(this->_impl)
+ .template onErrorCategory<category>(policy, std::forward<Func>(func)));
}
//
@@ -496,10 +499,10 @@ public:
*
* The callback takes a const T& and must return void.
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableExactR<void, Func, const T>)
- Future<T> tap(Func&& func) && noexcept {
- return Future<T>(std::move(this->_impl).tap(std::forward<Func>(func)));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const T>&& isFuturePolicy<Policy>)
+ Future<T> tap(Policy policy, Func&& func) && noexcept {
+ return Future<T>(std::move(this->_impl).tap(policy, std::forward<Func>(func)));
}
/**
@@ -509,10 +512,10 @@ public:
*
* The callback takes a non-OK Status and must return void.
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableExactR<void, Func, const Status>)
- Future<T> tapError(Func&& func) && noexcept {
- return Future<T>(std::move(this->_impl).tapError(std::forward<Func>(func)));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const Status>&& isFuturePolicy<Policy>)
+ Future<T> tapError(Policy policy, Func&& func) && noexcept {
+ return Future<T>(std::move(this->_impl).tapError(policy, std::forward<Func>(func)));
}
/**
@@ -523,10 +526,11 @@ public:
*
* The callback takes a StatusOrStatusWith<T> and must return void.
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableExactR<void, Func, const StatusOrStatusWith<T>>)
- Future<T> tapAll(Func&& func) && noexcept {
- return Future<T>(std::move(this->_impl).tapAll(std::forward<Func>(func)));
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const StatusOrStatusWith<T>>&&
+ isFuturePolicy<Policy>)
+ Future<T> tapAll(Policy policy, Func&& func) && noexcept {
+ return Future<T>(std::move(this->_impl).tapAll(policy, std::forward<Func>(func)));
}
// Calling this on a Future is never strictly necessary, since this is already an inline
@@ -534,6 +538,59 @@ public:
// unsafeToInlineFuture for some future types but not others.
using SemiFuture<T>::unsafeToInlineFuture;
+ /**
+ * Providing a Policy to continuation functions is optional. All callers should eventually
+ * use the default policy by removing the tag altogether.
+ *
+ * TODO(SERVER-66126): Remove all tag-taking methods and associated default implementations.
+ */
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>)
+ void getAsync(Func&& func) && noexcept {
+ std::move(*this).getAsync(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, T>)
+ auto then(Func&& func) && noexcept {
+ return std::move(*this).then(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>)
+ auto onCompletion(Func&& func) && noexcept {
+ return std::move(*this).onCompletion(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ auto onError(Func&& func) && noexcept {
+ return std::move(*this).onError(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(ErrorCodes::Error code, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ auto onError(Func&& func) && noexcept {
+ return std::move(*this).template onError<code>(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(ErrorCategory category, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ auto onErrorCategory(Func&& func) && noexcept {
+ return std::move(*this).template onErrorCategory<category>(destroyDefault,
+ std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const T>)
+ Future<T> tap(Func&& func) && noexcept {
+ return std::move(*this).tap(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const Status>)
+ Future<T> tapError(Func&& func) && noexcept {
+ return std::move(*this).tapError(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, const StatusOrStatusWith<T>>)
+ Future<T> tapAll(Func&& func) && noexcept {
+ return std::move(*this).tapAll(destroyDefault, std::forward<Func>(func));
+ }
+
private:
template <typename>
friend class ExecutorFuture;
@@ -636,15 +693,16 @@ public:
* Attach a completion callback to asynchronously consume this `ExecutorFuture`'s result.
* \see `Future<T>::getAsync()`.
*/
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>)
- void getAsync(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>&&
+ isFuturePolicy<Policy>)
+ void getAsync(Policy policy, Func&& func) && noexcept {
static_assert(std::is_void_v<decltype(func(std::declval<StatusOrStatusWith<T>>()))>,
"func passed to getAsync must return void");
// Can't use wrapCB since we don't want to return a future, just schedule a non-chainable
// callback.
- std::move(this->_impl).getAsync([
+ std::move(this->_impl).getAsync(policy, [
exec = std::move(_exec), // Unlike wrapCB this can move because we won't need it later.
func = std::forward<Func>(func)
](StatusOrStatusWith<T> arg) mutable noexcept {
@@ -656,46 +714,50 @@ public:
});
}
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallable<Func, T>)
- auto then(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallable<Func, T>&& isFuturePolicy<Policy>)
+ auto then(Policy policy, Func&& func) && noexcept {
return mongo::ExecutorFuture(
- std::move(_exec), std::move(this->_impl).then(wrapCB<T>(std::forward<Func>(func))));
+ std::move(_exec),
+ std::move(this->_impl).then(policy, wrapCB<T>(policy, std::forward<Func>(func))));
}
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>)
- auto onCompletion(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>&& isFuturePolicy<Policy>)
+ auto onCompletion(Policy policy, Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl)
- .onCompletion(wrapCB<StatusOrStatusWith<T>>(std::forward<Func>(func))));
+ .onCompletion(policy,
+ wrapCB<StatusOrStatusWith<T>>(policy, std::forward<Func>(func))));
}
- TEMPLATE(typename Func)
- REQUIRES(future_details::isCallableR<T, Func, Status>)
- ExecutorFuture<T> onError(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>&& isFuturePolicy<Policy>)
+ ExecutorFuture<T> onError(Policy policy, Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
- std::move(this->_impl).onError(wrapCB<Status>(std::forward<Func>(func))));
+ std::move(this->_impl)
+ .onError(policy, wrapCB<Status>(policy, std::forward<Func>(func))));
}
- TEMPLATE(ErrorCodes::Error code, typename Func)
- REQUIRES(future_details::isCallableR<T, Func, Status>)
- ExecutorFuture<T> onError(Func&& func) && noexcept {
+ TEMPLATE(ErrorCodes::Error code, typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>&& isFuturePolicy<Policy>)
+ ExecutorFuture<T> onError(Policy policy, Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl)
- .template onError<code>(wrapCB<Status>(std::forward<Func>(func))));
+ .template onError<code>(policy, wrapCB<Status>(policy, std::forward<Func>(func))));
}
- TEMPLATE(ErrorCategory category, typename Func)
- REQUIRES(future_details::isCallableR<T, Func, Status>)
- ExecutorFuture<T> onErrorCategory(Func&& func) && noexcept {
+ TEMPLATE(ErrorCategory category, typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>&& isFuturePolicy<Policy>)
+ ExecutorFuture<T> onErrorCategory(Policy policy, Func&& func) && noexcept {
return mongo::ExecutorFuture(
std::move(_exec),
std::move(this->_impl)
- .template onErrorCategory<category>(wrapCB<Status>(std::forward<Func>(func))));
+ .template onErrorCategory<category>(
+ policy, wrapCB<Status>(policy, std::forward<Func>(func))));
}
/**
@@ -706,6 +768,44 @@ public:
*/
using SemiFuture<T>::unsafeToInlineFuture;
+ /**
+ * Providing a Policy to continuation functions is optional. All callers should eventually
+ * use the default policy by removing the tag altogether.
+ *
+ * TODO(SERVER-66126): Remove all tag-taking methods and associated default implementations.
+ */
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableExactR<void, Func, StatusOrStatusWith<T>>)
+ void getAsync(Func&& func) && noexcept {
+ std::move(*this).getAsync(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, T>)
+ auto then(Func&& func) && noexcept {
+ return std::move(*this).then(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallable<Func, StatusOrStatusWith<T>>)
+ auto onCompletion(Func&& func) && noexcept {
+ return std::move(*this).onCompletion(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ auto onError(Func&& func) && noexcept {
+ return std::move(*this).onError(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(ErrorCodes::Error code, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ auto onError(Func&& func) && noexcept {
+ return std::move(*this).template onError<code>(destroyDefault, std::forward<Func>(func));
+ }
+ TEMPLATE(ErrorCategory category, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, Status>)
+ auto onErrorCategory(Func&& func) && noexcept {
+ return std::move(*this).template onErrorCategory<category>(destroyDefault,
+ std::forward<Func>(func));
+ }
+
private:
// This *must* take exec by ref to ensure it isn't moved from while evaluating wrapCB above.
ExecutorFuture(ExecutorPtr&& exec, Impl&& impl) : SemiFuture<T>(std::move(impl)), _exec(exec) {
@@ -717,8 +817,9 @@ private:
* Future<U>, then schedules a task on _exec to complete the associated promise with the result
* of calling func with that argument.
*/
- template <typename RawArg, typename Func>
- auto wrapCB(Func&& func) {
+ TEMPLATE(typename RawArg, typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ auto wrapCB(Policy policy, Func&& func) {
// Have to take care to never put void in argument position, since that is a hard error.
using Result = typename std::conditional_t<std::is_void_v<RawArg>,
std::invoke_result<Func>,
@@ -828,10 +929,15 @@ public:
* because this method will correctly propagate errors thrown from makeResult(), rather than
* ErrorCodes::BrokenPromise.
*/
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, void>&& isFuturePolicy<Policy>)
+ void setWith(Policy policy, Func&& func) noexcept {
+ setFrom(Future<void>::makeReady().then(policy, std::forward<Func>(func)));
+ }
TEMPLATE(typename Func)
REQUIRES(future_details::isCallableR<T, Func, void>)
void setWith(Func&& func) noexcept {
- setFrom(Future<void>::makeReady().then(std::forward<Func>(func)));
+ setWith(destroyDefault, std::forward<Func>(func));
}
/**
@@ -1116,10 +1222,15 @@ public:
return SharedSemiFuture<T>(_sharedState);
}
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(future_details::isCallableR<T, Func, void>&& isFuturePolicy<Policy>)
+ void setWith(Policy policy, Func&& func) noexcept {
+ setFrom(Future<void>::makeReady().then(policy, std::forward<Func>(func)));
+ }
TEMPLATE(typename Func)
REQUIRES(future_details::isCallableR<T, Func, void>)
void setWith(Func&& func) noexcept {
- setFrom(Future<void>::makeReady().then(std::forward<Func>(func)));
+ setWith(destroyDefault, std::forward<Func>(func));
}
/**
diff --git a/src/mongo/util/future_impl.h b/src/mongo/util/future_impl.h
index b1ef0ffd955..a2a5d963675 100644
--- a/src/mongo/util/future_impl.h
+++ b/src/mongo/util/future_impl.h
@@ -52,6 +52,47 @@
namespace mongo {
+struct FuturePolicy {};
+
+template <typename T>
+inline constexpr bool isFuturePolicy = std::is_base_of_v<FuturePolicy, T>;
+
+/**
+ * Transitional tags for specifying destruction order semantics for continuations.
+ * The long-term goal is to eliminate `destroyWeak` via manual review of existing
+ * continuations.
+ * A continuation can be switched to `destroyStrong` when it has been determined
+ * that subsequent continuations do not depend on the lifetime of its captures.
+ * The plan is to remove these transitional tags altogether after _all_ continuations
+ * have been thus converted to the strong semantics specification.
+ */
+template <bool strongCleanupValue>
+struct CleanupFuturePolicy : FuturePolicy {
+ static constexpr bool strongCleanup = strongCleanupValue;
+};
+using WeakFuturePolicy = CleanupFuturePolicy<false>;
+using StrongFuturePolicy = CleanupFuturePolicy<true>;
+
+/**
+ * The passed-in continuation function may or may not be cleared
+ * immediately after the function runs. In some contexts the entire
+ * continuation chain will run and callbacks are destroyed as the stack
+ * unwinds. In other contexts, each stage of the continuation will destroy its
+ * callback immediately following execution.
+ */
+inline constexpr WeakFuturePolicy destroyWeak{};
+
+/**
+ * The passed-in continuation function will always be cleared
+ * immediately after the function runs, and before the subsequent continuation runs.
+ */
+inline constexpr StrongFuturePolicy destroyStrong{};
+
+/**
+ * Used by Future implementation details to apply a consistent default FuturePolicy.
+ */
+inline constexpr WeakFuturePolicy destroyDefault{};
+
template <typename T>
class Promise;
@@ -881,8 +922,9 @@ public:
return _shared.getNoThrow(interruptible);
}
- template <typename Func>
- void getAsync(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ void getAsync(Policy policy, Func&& func) && noexcept {
static_assert(std::is_void<decltype(call(func, std::declval<StatusWith<T>>()))>::value,
"func passed to getAsync must return void");
@@ -905,8 +947,9 @@ public:
});
}
- template <typename Func>
- auto then(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ auto then(Policy policy, Func&& func) && noexcept {
using Result = NormalizedCallResult<Func, T>;
if constexpr (!isFutureLike<Result>) {
return generalImpl(
@@ -959,8 +1002,9 @@ public:
}
}
- template <typename Func>
- auto onCompletion(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ auto onCompletion(Policy policy, Func&& func) && noexcept {
using Wrapper = StatusOrStatusWith<T>;
using Result = NormalizedCallResult<Func, StatusOrStatusWith<T>>;
if constexpr (!isFutureLike<Result>) {
@@ -1034,8 +1078,9 @@ public:
}
}
- template <typename Func>
- FutureImpl<FakeVoidToVoid<T>> onError(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ FutureImpl<FakeVoidToVoid<T>> onError(Policy policy, Func&& func) && noexcept {
using Result = NormalizedCallResult<Func, Status>;
static_assert(
std::is_same<VoidToFakeVoid<UnwrappedType<Result>>, T>::value,
@@ -1088,8 +1133,9 @@ public:
}
}
- template <ErrorCodes::Error code, typename Func>
- FutureImpl<FakeVoidToVoid<T>> onError(Func&& func) && noexcept {
+ TEMPLATE(ErrorCodes::Error code, typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ FutureImpl<FakeVoidToVoid<T>> onError(Policy policy, Func&& func) && noexcept {
using Result = NormalizedCallResult<Func, Status>;
static_assert(
std::is_same_v<UnwrappedType<Result>, FakeVoidToVoid<T>>,
@@ -1100,15 +1146,17 @@ public:
// TODO in C++17 with constexpr if this can be done cleaner and more efficiently by not
// throwing.
- return std::move(*this).onError([func = std::forward<Func>(func)](Status&& status) mutable {
- if (status != code)
- uassertStatusOK(status);
- return throwingCall(func, std::move(status));
- });
+ return std::move(*this).onError(policy,
+ [func = std::forward<Func>(func)](Status&& status) mutable {
+ if (status != code)
+ uassertStatusOK(status);
+ return throwingCall(func, std::move(status));
+ });
}
- template <ErrorCategory category, typename Func>
- FutureImpl<FakeVoidToVoid<T>> onErrorCategory(Func&& func) && noexcept {
+ TEMPLATE(ErrorCategory category, typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ FutureImpl<FakeVoidToVoid<T>> onErrorCategory(Policy policy, Func&& func) && noexcept {
using Result = NormalizedCallResult<Func, Status>;
static_assert(std::is_same_v<UnwrappedType<Result>, FakeVoidToVoid<T>>,
"func passed to Future<T>::onErrorCategory must return T, StatusWith<T>, "
@@ -1117,15 +1165,17 @@ public:
if (_immediate || (isReady() && _shared->status.isOK()))
return std::move(*this);
- return std::move(*this).onError([func = std::forward<Func>(func)](Status&& status) mutable {
- if (!ErrorCodes::isA<category>(status))
- uassertStatusOK(status);
- return throwingCall(func, std::move(status));
- });
+ return std::move(*this).onError(policy,
+ [func = std::forward<Func>(func)](Status&& status) mutable {
+ if (!ErrorCodes::isA<category>(status))
+ uassertStatusOK(status);
+ return throwingCall(func, std::move(status));
+ });
}
- template <typename Func>
- FutureImpl<FakeVoidToVoid<T>> tap(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ FutureImpl<FakeVoidToVoid<T>> tap(Policy policy, Func&& func) && noexcept {
static_assert(std::is_void<decltype(call(func, std::declval<const T&>()))>::value,
"func passed to tap must return void");
@@ -1134,8 +1184,9 @@ public:
[](Func && func, const Status& status) noexcept {});
}
- template <typename Func>
- FutureImpl<FakeVoidToVoid<T>> tapError(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ FutureImpl<FakeVoidToVoid<T>> tapError(Policy policy, Func&& func) && noexcept {
static_assert(std::is_void<decltype(call(func, std::declval<const Status&>()))>::value,
"func passed to tapError must return void");
@@ -1143,8 +1194,9 @@ public:
](Func && func, const Status& status) noexcept { call(func, status); });
}
- template <typename Func>
- FutureImpl<FakeVoidToVoid<T>> tapAll(Func&& func) && noexcept {
+ TEMPLATE(typename Policy, typename Func)
+ REQUIRES(isFuturePolicy<Policy>)
+ FutureImpl<FakeVoidToVoid<T>> tapAll(Policy policy, Func&& func) && noexcept {
static_assert(
std::is_void<decltype(call(func, std::declval<const StatusOrStatusWith<T>&>()))>::value,
"func passed to tapAll must return void");
@@ -1340,7 +1392,7 @@ private:
template <typename T>
inline FutureImpl<void> FutureImpl<T>::ignoreValue() && noexcept {
- return std::move(*this).then([](auto&&) {});
+ return std::move(*this).then(destroyDefault, [](auto&&) {});
}
} // namespace future_details