summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Ma <jonathan.ma@mongodb.com>2019-01-25 14:37:32 -0500
committerJonathan Ma <jonathan.ma@mongodb.com>2019-01-25 17:11:53 -0500
commit6d1458c964e600a07b183d0d5331eff0bcab9493 (patch)
tree3f56f6b40ba565328a56d92366f83e97e41bd7e4
parentffe35fbcb46d344fef138fe82dfbedc070205ab8 (diff)
downloadmongo-6d1458c964e600a07b183d0d5331eff0bcab9493.tar.gz
SERVER-38368 Allow handling ErrorCategory types for Futures error handling
-rw-r--r--src/mongo/util/future.h23
-rw-r--r--src/mongo/util/future_test_future_int.cpp44
2 files changed, 67 insertions, 0 deletions
diff --git a/src/mongo/util/future.h b/src/mongo/util/future.h
index 3033bec5687..c32731c1986 100644
--- a/src/mongo/util/future.h
+++ b/src/mongo/util/future.h
@@ -1097,6 +1097,29 @@ public:
}
/**
+ * Similar to the first two onErrors, but only calls the callback if the category matches
+ * the template parameter. Otherwise lets the error propagate unchanged.
+ */
+ template <ErrorCategory category, typename Func>
+ Future<T> onErrorCategory(Func&& func) && noexcept {
+ using Result = RawNormalizedCallResult<Func, Status>;
+ static_assert(
+ std::is_same<Result, T>::value || std::is_same<Result, Future<T>>::value ||
+ (std::is_same<T, FakeVoid>::value && std::is_same<Result, Future<void>>::value),
+ "func passed to Future<T>::onErrorCategory must return T, StatusWith<T>, or Future<T>");
+
+ 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.code()))
+ uassertStatusOK(status);
+ return throwingCall(func, std::move(status));
+ });
+ }
+
+ /**
* TODO do we need a version of then/onError like onCompletion() that handles both success and
* Failure, but doesn't end the chain like getAsync()? Right now we don't, and we can add one if
* we do.
diff --git a/src/mongo/util/future_test_future_int.cpp b/src/mongo/util/future_test_future_int.cpp
index 48f6335421c..6e4395952a7 100644
--- a/src/mongo/util/future_test_future_int.cpp
+++ b/src/mongo/util/future_test_future_int.cpp
@@ -529,6 +529,50 @@ TEST(Future, Fail_onErrorCodeMismatch) {
});
}
+TEST(Future, Success_onErrorCategory) {
+ FUTURE_SUCCESS_TEST([] { return 1; },
+ [](Future<int>&& fut) {
+ ASSERT_EQ(
+ std::move(fut)
+ .onErrorCategory<ErrorCategory::NetworkError>([](Status) {
+ FAIL("onErrorCategory<category>() callback was called");
+ return 0;
+ })
+ .then([](int i) { return i + 2; })
+ .get(),
+ 3);
+ });
+}
+
+TEST(Future, Fail_onErrorCategoryMatch) {
+ FUTURE_FAIL_TEST<int>([](Future<int>&& fut) {
+ auto res = std::move(fut)
+ .onError([](Status s) {
+ ASSERT_EQ(s, failStatus());
+ return StatusWith<int>(ErrorCodes::HostUnreachable, "");
+ })
+ .onErrorCategory<ErrorCategory::NetworkError>(
+ [](Status&&) { return StatusWith<int>(3); })
+ .getNoThrow();
+ ASSERT_EQ(res, 3);
+ });
+}
+
+TEST(Future, Fail_onErrorCategoryMismatch) {
+ FUTURE_FAIL_TEST<int>([](Future<int>&& fut) {
+ ASSERT_EQ(std::move(fut)
+ .onErrorCategory<ErrorCategory::NetworkError>([](Status s) -> int {
+ FAIL("Why was this called?") << s;
+ MONGO_UNREACHABLE;
+ })
+ .onError([](Status s) {
+ ASSERT_EQ(s, failStatus());
+ return 3;
+ })
+ .getNoThrow(),
+ 3);
+ });
+}
TEST(Future, Success_tap) {
FUTURE_SUCCESS_TEST([] { return 1; },