fix shadowed variables
[folly.git] / folly / wangle / futures / Future-inl.h
index f2816b487e694035d215c654b36c3aef9d93d77b..31cca9e51a3ad1f9a5ca39d4e8d94b22ce2b3315 100644 (file)
 #include <chrono>
 #include <thread>
 
-#include <folly/wangle/futures/detail/Core.h>
 #include <folly/Baton.h>
+#include <folly/wangle/futures/detail/Core.h>
+#include <folly/wangle/futures/Timekeeper.h>
 
 namespace folly { namespace wangle {
 
+class Timekeeper;
+
+namespace detail {
+  Timekeeper* getTimekeeperSingleton();
+}
+
 template <typename T>
 struct isFuture {
   static const bool value = false;
@@ -124,8 +131,8 @@ Future<T>::then(F&& func) {
   setCallback_(
     [p, funcm](Try<T>&& t) mutable {
       p->fulfil([&]() {
-          return (*funcm)(std::move(t));
-        });
+        return (*funcm)(std::move(t));
+      });
     });
 
   return std::move(f);
@@ -152,7 +159,7 @@ Future<T>::then(F&& func) {
   setCallback_(
     [p, funcm](Try<T>&& t) mutable {
       if (t.hasException()) {
-        p->setException(t.getException());
+        p->setException(std::move(t.exception()));
       } else {
         p->fulfil([&]() {
           return (*funcm)(std::move(t.value()));
@@ -182,7 +189,7 @@ Future<T>::then(F&& func) {
   setCallback_(
     [p, funcm](Try<T>&& t) mutable {
       if (t.hasException()) {
-        p->setException(t.getException());
+        p->setException(std::move(t.exception()));
       } else {
         p->fulfil([&]() {
           return (*funcm)();
@@ -217,10 +224,12 @@ Future<T>::then(F&& func) {
         auto f2 = (*funcm)(std::move(t));
         // that didn't throw, now we can steal p
         f2.setCallback_([p](Try<B>&& b) mutable {
-            p->fulfilTry(std::move(b));
-          });
+          p->fulfilTry(std::move(b));
+        });
+      } catch (const std::exception& e) {
+        p->setException(exception_wrapper(std::current_exception(), e));
       } catch (...) {
-        p->setException(std::current_exception());
+        p->setException(exception_wrapper(std::current_exception()));
       }
     });
 
@@ -248,15 +257,17 @@ Future<T>::then(F&& func) {
   setCallback_(
     [p, funcm](Try<T>&& t) mutable {
       if (t.hasException()) {
-        p->setException(t.getException());
+        p->setException(std::move(t.exception()));
       } else {
         try {
           auto f2 = (*funcm)(std::move(t.value()));
           f2.setCallback_([p](Try<B>&& b) mutable {
-              p->fulfilTry(std::move(b));
-            });
+            p->fulfilTry(std::move(b));
+          });
+        } catch (const std::exception& e) {
+          p->setException(exception_wrapper(std::current_exception(), e));
         } catch (...) {
-          p->setException(std::current_exception());
+          p->setException(exception_wrapper(std::current_exception()));
         }
       }
     });
@@ -284,15 +295,17 @@ Future<T>::then(F&& func) {
   setCallback_(
     [p, funcm](Try<T>&& t) mutable {
       if (t.hasException()) {
-        p->setException(t.getException());
+        p->setException(t.exception());
       } else {
         try {
           auto f2 = (*funcm)();
           f2.setCallback_([p](Try<B>&& b) mutable {
-              p->fulfilTry(std::move(b));
-            });
+            p->fulfilTry(std::move(b));
+          });
+        } catch (const std::exception& e) {
+          p->setException(exception_wrapper(std::current_exception(), e));
         } catch (...) {
-          p->setException(std::current_exception());
+          p->setException(exception_wrapper(std::current_exception()));
         }
       }
     });
@@ -322,17 +335,13 @@ Future<T>::onError(F&& func) {
   auto pm = folly::makeMoveWrapper(std::move(p));
   auto funcm = folly::makeMoveWrapper(std::move(func));
   setCallback_([pm, funcm](Try<T>&& t) mutable {
-    try {
-      t.throwIfFailed();
-    } catch (Exn& e) {
-      pm->fulfil([&]{
-        return (*funcm)(e);
-      });
-      return;
-    } catch (...) {
-      // fall through
+    if (!t.template withException<Exn>([&] (Exn& e) {
+          pm->fulfil([&]{
+            return (*funcm)(e);
+          });
+        })) {
+      pm->fulfilTry(std::move(t));
     }
-    pm->fulfilTry(std::move(t));
   });
 
   return f;
@@ -355,22 +364,20 @@ Future<T>::onError(F&& func) {
   auto pm = folly::makeMoveWrapper(std::move(p));
   auto funcm = folly::makeMoveWrapper(std::move(func));
   setCallback_([pm, funcm](Try<T>&& t) mutable {
-    try {
-      t.throwIfFailed();
-    } catch (Exn& e) {
-      try {
-        auto f2 = (*funcm)(e);
-        f2.setCallback_([pm](Try<T>&& t2) mutable {
-          pm->fulfilTry(std::move(t2));
-        });
-      } catch (...) {
-        pm->setException(std::current_exception());
-      }
-      return;
-    } catch (...) {
-      // fall through
+    if (!t.template withException<Exn>([&] (Exn& e) {
+          try {
+            auto f2 = (*funcm)(e);
+            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()));
+          }
+        })) {
+      pm->fulfilTry(std::move(t));
     }
-    pm->fulfilTry(std::move(t));
   });
 
   return f;
@@ -426,8 +433,8 @@ bool Future<T>::isReady() const {
 }
 
 template <class T>
-void Future<T>::raise(std::exception_ptr exception) {
-  core_->raise(exception);
+void Future<T>::raise(exception_wrapper exception) {
+  core_->raise(std::move(exception));
 }
 
 // makeFuture
@@ -476,32 +483,38 @@ Future<T> makeFuture(std::exception_ptr const& e) {
   return std::move(f);
 }
 
+template <class T>
+Future<T> makeFuture(exception_wrapper ew) {
+  Promise<T> p;
+  p.setException(std::move(ew));
+  return p.getFuture();
+}
+
 template <class T, class E>
 typename std::enable_if<std::is_base_of<std::exception, E>::value,
                         Future<T>>::type
 makeFuture(E const& e) {
   Promise<T> p;
   auto f = p.getFuture();
-  p.fulfil([&]() -> T { throw e; });
+  p.setException(make_exception_wrapper<E>(e));
   return std::move(f);
 }
 
 template <class T>
 Future<T> makeFuture(Try<T>&& t) {
-  try {
+  if (t.hasException()) {
+    return makeFuture<T>(std::move(t.exception()));
+  } else {
     return makeFuture<T>(std::move(t.value()));
-  } catch (...) {
-    return makeFuture<T>(std::current_exception());
   }
 }
 
 template <>
 inline Future<void> makeFuture(Try<void>&& t) {
-  try {
-    t.throwIfFailed();
+  if (t.hasException()) {
+    return makeFuture<void>(std::move(t.exception()));
+  } else {
     return makeFuture();
-  } catch (...) {
-    return makeFuture<void>(std::current_exception());
   }
 }
 
@@ -670,9 +683,9 @@ inline Future<void> waitWithSemaphore<void>(Future<void>&& f) {
   return done;
 }
 
-template <typename T, class Duration>
+template <typename T, class Dur>
 Future<T>
-waitWithSemaphore(Future<T>&& f, Duration timeout) {
+waitWithSemaphore(Future<T>&& f, Dur timeout) {
   auto baton = std::make_shared<Baton<>>();
   auto done = f.then([baton](Try<T> &&t) {
     baton->post();
@@ -682,9 +695,9 @@ waitWithSemaphore(Future<T>&& f, Duration timeout) {
   return done;
 }
 
-template <class Duration>
+template <class Dur>
 Future<void>
-waitWithSemaphore(Future<void>&& f, Duration timeout) {
+waitWithSemaphore(Future<void>&& f, Dur timeout) {
   auto baton = std::make_shared<Baton<>>();
   auto done = f.then([baton](Try<void> &&t) {
     baton->post();
@@ -694,6 +707,138 @@ waitWithSemaphore(Future<void>&& f, Duration timeout) {
   return done;
 }
 
+namespace {
+  template <class T>
+  void getWaitHelper(Future<T>* f) {
+    // If we already have a value do the cheap thing
+    if (f->isReady()) {
+      return;
+    }
+
+    folly::Baton<> baton;
+    f->then([&](Try<T> const&) {
+      baton.post();
+    });
+    baton.wait();
+  }
+
+  template <class T>
+  Future<T> getWaitTimeoutHelper(Future<T>* f, Duration dur) {
+    // TODO make and use variadic whenAny #5877971
+    Promise<T> p;
+    auto token = std::make_shared<std::atomic<bool>>();
+    folly::Baton<> baton;
+
+    folly::wangle::detail::getTimekeeperSingleton()->after(dur)
+      .then([&,token](Try<void> const& t) {
+        if (token->exchange(true) == false) {
+          try {
+            t.value();
+            p.setException(TimedOut());
+          } catch (std::exception const& e) {
+            p.setException(exception_wrapper(std::current_exception(), e));
+          } catch (...) {
+            p.setException(exception_wrapper(std::current_exception()));
+          }
+          baton.post();
+        }
+      });
+
+    f->then([&, token](Try<T>&& t) {
+      if (token->exchange(true) == false) {
+        p.fulfilTry(std::move(t));
+        baton.post();
+      }
+    });
+
+    baton.wait();
+    return p.getFuture();
+  }
+}
+
+template <class T>
+T Future<T>::get() {
+  getWaitHelper(this);
+
+  // Big assumption here: the then() call above, since it doesn't move out
+  // the value, leaves us with a value to return here. This would be a big
+  // no-no in user code, but I'm invoking internal developer privilege. This
+  // is slightly more efficient (save a move()) especially if there's an
+  // exception (save a throw).
+  return std::move(value());
+}
+
+template <>
+inline void Future<void>::get() {
+  getWaitHelper(this);
+  value();
+}
+
+template <class T>
+T Future<T>::get(Duration dur) {
+  return std::move(getWaitTimeoutHelper(this, dur).value());
+}
+
+template <>
+inline void Future<void>::get(Duration dur) {
+  getWaitTimeoutHelper(this, dur).value();
+}
+
+template <class T>
+Future<T> Future<T>::within(Duration dur, Timekeeper* tk) {
+  return within(dur, TimedOut(), tk);
+}
+
+template <class T>
+template <class E>
+Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
+
+  struct Context {
+    Context(E ex) : exception(std::move(ex)), promise(), token(false) {}
+    E exception;
+    Promise<T> promise;
+    std::atomic<bool> token;
+  };
+  auto ctx = std::make_shared<Context>(std::move(e));
+
+  if (!tk) {
+    tk = folly::wangle::detail::getTimekeeperSingleton();
+  }
+
+  tk->after(dur)
+    .then([ctx](Try<void> const& t) {
+      if (ctx->token.exchange(true) == false) {
+        try {
+          t.throwIfFailed();
+          ctx->promise.setException(std::move(ctx->exception));
+        } catch (std::exception const& e2) {
+          ctx->promise.setException(
+              exception_wrapper(std::current_exception(), e2));
+        } catch (...) {
+          ctx->promise.setException(
+              exception_wrapper(std::current_exception()));
+        }
+      }
+    });
+
+  this->then([ctx](Try<T>&& t) {
+    if (ctx->token.exchange(true) == false) {
+      ctx->promise.fulfilTry(std::move(t));
+    }
+  });
+
+  return ctx->promise.getFuture();
+}
+
+template <class T>
+Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
+  return whenAll(*this, futures::sleep(dur, tk))
+    .then([](Try<std::tuple<Try<T>, Try<void>>>&& tup) {
+      Try<T>& t = std::get<0>(tup.value());
+      return makeFuture<T>(std::move(t));
+    });
+}
+
 }}
 
 // I haven't included a Future<T&> specialization because I don't forsee us