Let Future::then call callbacks outside of the catch handler
authorYedidya Feldblum <yfeldblum@fb.com>
Fri, 30 Dec 2016 01:25:26 +0000 (17:25 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Fri, 30 Dec 2016 01:32:53 +0000 (17:32 -0800)
Summary:
[Folly] Let `Future::then` call callbacks outside of the catch handler.

And `Future::onError`.

This makes the behavior of calls to `Future` callbacks with respect to currently-handled ("active") exceptions consistent - there will not be an active exception by the time the `Future` callback is called. (Unless `Future::then` or `Future::onError`, etc., is itself called with an active exception. Or unless the `Promise` is fulfilled, outside of the `Future` implementation code, with an active exception.)

This will affect any code which tries to call `std::current_exception()` or `throw;` from within a `Future` callback, such as an `onError` handler. That code will crash. (It was incorrect anyway, and relied on misusing Folly Futures.)

Reviewed By: ericniebler

Differential Revision: D4372173

fbshipit-source-id: 600b22e4db63c98358de29a6abcee807fbc53b0f

folly/futures/Future-inl.h

index 52b650c016d5499a1286accb310a8726cdc49ddb..2d19c67d0a65b09c62b484f2517a5fb85e1b27e5 100644 (file)
@@ -205,20 +205,26 @@ Future<T>::thenImplementation(F&& func, detail::argResult<isTry, F, Args...>) {
 
   setCallback_([ funcm = std::forward<F>(func), pm = std::move(p) ](
       Try<T> && t) mutable {
-    if (!isTry && t.hasException()) {
-      pm.setException(std::move(t.exception()));
-    } else {
-      try {
-        auto f2 = funcm(t.template get<isTry, Args>()...);
-        // that didn't throw, now we can steal p
-        f2.setCallback_([p = std::move(pm)](Try<B> && b) mutable {
-          p.setTry(std::move(b));
-        });
-      } catch (const std::exception& e) {
-        pm.setException(exception_wrapper(std::current_exception(), e));
-      } catch (...) {
-        pm.setException(exception_wrapper(std::current_exception()));
+    auto ew = [&] {
+      if (!isTry && t.hasException()) {
+        return std::move(t.exception());
+      } else {
+        try {
+          auto f2 = funcm(t.template get<isTry, Args>()...);
+          // that didn't throw, now we can steal p
+          f2.setCallback_([p = std::move(pm)](Try<B> && b) mutable {
+            p.setTry(std::move(b));
+          });
+          return exception_wrapper();
+        } catch (const std::exception& e) {
+          return exception_wrapper(std::current_exception(), e);
+        } catch (...) {
+          return exception_wrapper(std::current_exception());
+        }
       }
+    }();
+    if (ew) {
+      pm.setException(std::move(ew));
     }
   });
 
@@ -301,15 +307,21 @@ Future<T>::onError(F&& func) {
   setCallback_([ pm = std::move(p), funcm = std::forward<F>(func) ](
       Try<T> && t) mutable {
     if (!t.template withException<Exn>([&](Exn& e) {
-          try {
-            auto f2 = funcm(e);
-            f2.setCallback_([pm = std::move(pm)](Try<T> && t2) mutable {
-              pm.setTry(std::move(t2));
-            });
-          } catch (const std::exception& e2) {
-            pm.setException(exception_wrapper(std::current_exception(), e2));
-          } catch (...) {
-            pm.setException(exception_wrapper(std::current_exception()));
+          auto ew = [&] {
+            try {
+              auto f2 = funcm(e);
+              f2.setCallback_([pm = std::move(pm)](Try<T> && t2) mutable {
+                pm.setTry(std::move(t2));
+              });
+              return exception_wrapper();
+            } catch (const std::exception& e2) {
+              return exception_wrapper(std::current_exception(), e2);
+            } catch (...) {
+              return exception_wrapper(std::current_exception());
+            }
+          }();
+          if (ew) {
+            pm.setException(std::move(ew));
           }
         })) {
       pm.setTry(std::move(t));
@@ -350,15 +362,21 @@ Future<T>::onError(F&& func) {
   setCallback_(
       [ pm = std::move(p), funcm = std::forward<F>(func) ](Try<T> t) mutable {
         if (t.hasException()) {
-          try {
-            auto f2 = funcm(std::move(t.exception()));
-            f2.setCallback_([pm = std::move(pm)](Try<T> t2) mutable {
-              pm.setTry(std::move(t2));
-            });
-          } catch (const std::exception& e2) {
-            pm.setException(exception_wrapper(std::current_exception(), e2));
-          } catch (...) {
-            pm.setException(exception_wrapper(std::current_exception()));
+          auto ew = [&] {
+            try {
+              auto f2 = funcm(std::move(t.exception()));
+              f2.setCallback_([pm = std::move(pm)](Try<T> t2) mutable {
+                pm.setTry(std::move(t2));
+              });
+              return exception_wrapper();
+            } catch (const std::exception& e2) {
+              return exception_wrapper(std::current_exception(), e2);
+            } catch (...) {
+              return exception_wrapper(std::current_exception());
+            }
+          }();
+          if (ew) {
+            pm.setException(std::move(ew));
           }
         } else {
           pm.setTry(std::move(t));