onError(exception_wrapper)
authorJames Sedgwick <jsedgwick@fb.com>
Wed, 15 Apr 2015 17:11:47 +0000 (10:11 -0700)
committerAlecs King <int@fb.com>
Mon, 27 Apr 2015 23:41:03 +0000 (16:41 -0700)
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

folly/futures/Future-inl.h
folly/futures/Future.h
folly/futures/test/FutureTest.cpp

index dc842741bb1b0f81203c95e98300ae935a25ccd2..ffa22ad6d7d8b645225d0f5b918b621b181d530f 100644 (file)
@@ -244,6 +244,7 @@ Future<void> Future<T>::then() {
 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) {
@@ -273,6 +274,7 @@ Future<T>::onError(F&& func) {
 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) {
@@ -323,6 +325,70 @@ Future<T> Future<T>::onTimeout(Duration dur, F&& func, Timekeeper* tk) {
     .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>
 typename std::add_lvalue_reference<T>::type Future<T>::value() {
   throwIfInvalid();
index 18649726f3e91f4586dd6fdacfdf3a9937e0fe76..6919fd4e32a89e049ba802eb263829895576b489 100644 (file)
@@ -344,6 +344,7 @@ class Future {
   ///   });
   template <class F>
   typename std::enable_if<
+    !detail::callableWith<F, exception_wrapper>::value &&
     !detail::Extract<F>::ReturnsFuture::value,
     Future<T>>::type
   onError(F&& func);
@@ -351,10 +352,27 @@ class Future {
   /// Overload of onError where the error callback returns a 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 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
   /// the value/exception is passed through to the resulting Future.
   /// func shouldn't throw, but if it does it will be captured and propagated,
index 5a38c18b4d4468d86dcc5f198847a34332cc21ed..01e637699bbfab69b55eed58b6c9f199363d430b 100644 (file)
@@ -260,6 +260,66 @@ TEST(Future, onError) {
       .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
     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) {