Nuke Future<void> (folly/futures)
authorHans Fugal <fugalh@fb.com>
Wed, 1 Jul 2015 22:42:23 +0000 (15:42 -0700)
committerSara Golemon <sgolemon@fb.com>
Wed, 1 Jul 2015 23:24:54 +0000 (16:24 -0700)
Summary: Nuke Future<void> in favor of Future<Unit>, for the `folly/futures` subdirectory.

Reviewed By: @djwatson

Differential Revision: D2201259

29 files changed:
folly/futures/Future-inl.h
folly/futures/Future-pre.h
folly/futures/Future.cpp
folly/futures/Future.h
folly/futures/Promise.h
folly/futures/SharedPromise.h
folly/futures/Timekeeper.h
folly/futures/Try-inl.h
folly/futures/Try.h
folly/futures/Unit.h
folly/futures/detail/ThreadWheelTimekeeper.cpp
folly/futures/detail/ThreadWheelTimekeeper.h
folly/futures/helpers.h
folly/futures/test/Benchmark.cpp
folly/futures/test/CollectTest.cpp
folly/futures/test/ContextTest.cpp
folly/futures/test/CoreTest.cpp
folly/futures/test/ExecutorTest.cpp
folly/futures/test/FutureTest.cpp
folly/futures/test/InterruptTest.cpp
folly/futures/test/MapTest.cpp
folly/futures/test/PollTest.cpp
folly/futures/test/PromiseTest.cpp
folly/futures/test/TimekeeperTest.cpp
folly/futures/test/TryTest.cpp
folly/futures/test/UnitTest.cpp
folly/futures/test/ViaTest.cpp
folly/futures/test/WaitTest.cpp
folly/futures/test/WindowTest.cpp

index 9cacf19..0595310 100644 (file)
@@ -51,7 +51,7 @@ Future<T>::Future(T2&& val)
 template <class T>
 template <typename, typename>
 Future<T>::Future()
-  : core_(new detail::Core<T>(Try<T>())) {}
+  : core_(new detail::Core<T>(Try<T>(T()))) {}
 
 template <class T>
 Future<T>::~Future() {
@@ -226,7 +226,7 @@ auto Future<T>::then(Executor* x, Arg&& arg, Args&&... args)
 }
 
 template <class T>
-Future<void> Future<T>::then() {
+Future<Unit> Future<T>::then() {
   return then([] () {});
 }
 
@@ -432,8 +432,6 @@ inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
 template <class Func>
 auto via(Executor* x, Func func)
   -> Future<typename isFuture<decltype(func())>::Inner>
-// this would work, if not for Future<void> :-/
-// -> decltype(via(x).then(func))
 {
   // TODO make this actually more performant. :-P #7260175
   return via(x).then(func);
@@ -468,24 +466,29 @@ Future<typename std::decay<T>::type> makeFuture(T&& t) {
 }
 
 inline // for multiple translation units
-Future<void> makeFuture() {
-  return makeFuture(Try<void>());
+Future<Unit> makeFuture() {
+  return makeFuture(Unit{});
 }
 
+// XXX why is the dual necessary here? Can't we just use perfect forwarding
+// and capture func by reference always?
 template <class F>
 auto makeFutureWith(
     F&& func,
     typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
-    -> Future<decltype(func())> {
-  return makeFuture(makeTryWith([&func]() {
+    -> Future<typename Unit::Lift<decltype(func())>::type> {
+  using LiftedResult = typename Unit::Lift<decltype(func())>::type;
+  return makeFuture<LiftedResult>(makeTryWith([&func]() {
     return (func)();
   }));
 }
 
 template <class F>
-auto makeFutureWith(F const& func) -> Future<decltype(func())> {
+auto makeFutureWith(F const& func)
+    -> Future<typename Unit::Lift<decltype(func())>::type> {
   F copy = func;
-  return makeFuture(makeTryWith(std::move(copy)));
+  using LiftedResult = typename Unit::Lift<decltype(func())>::type;
+  return makeFuture<LiftedResult>(makeTryWith(std::move(copy)));
 }
 
 template <class T>
@@ -511,7 +514,7 @@ Future<T> makeFuture(Try<T>&& t) {
 }
 
 // via
-Future<void> via(Executor* executor, int8_t priority) {
+Future<Unit> via(Executor* executor, int8_t priority) {
   return makeFuture().via(executor, priority);
 }
 
@@ -603,14 +606,6 @@ struct CollectContext {
   std::atomic<bool> threw {false};
 };
 
-// Specialize for void (implementations in Future.cpp)
-
-template <>
-CollectContext<void>::~CollectContext();
-
-template <>
-void CollectContext<void>::setPartialResult(size_t i, Try<void>& t);
-
 }
 
 template <class InputIterator>
@@ -884,7 +879,7 @@ Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
   }
 
   tk->after(dur)
-    .then([ctx](Try<void> const& t) {
+    .then([ctx](Try<Unit> const& t) {
       if (ctx->token.exchange(true) == false) {
         if (t.hasException()) {
           ctx->promise.setException(std::move(t.exception()));
@@ -908,7 +903,7 @@ Future<T> Future<T>::within(Duration dur, E e, Timekeeper* tk) {
 template <class T>
 Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
   return collectAll(*this, futures::sleep(dur, tk))
-    .then([](std::tuple<Try<T>, Try<void>> tup) {
+    .then([](std::tuple<Try<T>, Try<Unit>> tup) {
       Try<T>& t = std::get<0>(tup);
       return makeFuture<T>(std::move(t));
     });
@@ -1007,11 +1002,6 @@ T Future<T>::get() {
   return std::move(wait().value());
 }
 
-template <>
-inline void Future<void>::get() {
-  wait().value();
-}
-
 template <class T>
 T Future<T>::get(Duration dur) {
   wait(dur);
@@ -1022,26 +1012,11 @@ T Future<T>::get(Duration dur) {
   }
 }
 
-template <>
-inline void Future<void>::get(Duration dur) {
-  wait(dur);
-  if (isReady()) {
-    return;
-  } else {
-    throw TimedOut();
-  }
-}
-
 template <class T>
 T Future<T>::getVia(DrivableExecutor* e) {
   return std::move(waitVia(e).value());
 }
 
-template <>
-inline void Future<void>::getVia(DrivableExecutor* e) {
-  waitVia(e).value();
-}
-
 namespace detail {
   template <class T>
   struct TryEquals {
@@ -1049,13 +1024,6 @@ namespace detail {
       return t1.value() == t2.value();
     }
   };
-
-  template <>
-  struct TryEquals<void> {
-    static bool equals(const Try<void>& t1, const Try<void>& t2) {
-      return true;
-    }
-  };
 }
 
 template <class T>
@@ -1134,7 +1102,7 @@ namespace futures {
 }
 
 // Instantiate the most common Future types to save compile time
-extern template class Future<void>;
+extern template class Future<Unit>;
 extern template class Future<bool>;
 extern template class Future<int>;
 extern template class Future<int64_t>;
@@ -1142,8 +1110,3 @@ extern template class Future<std::string>;
 extern template class Future<double>;
 
 } // namespace folly
-
-// I haven't included a Future<T&> specialization because I don't forsee us
-// using it, however it is not difficult to add when needed. Refer to
-// Future<void> for guidance. std::future and boost::future code would also be
-// instructive.
index f76c24a..f1a07d5 100644 (file)
@@ -24,7 +24,7 @@ template <class> class Promise;
 
 template <typename T>
 struct isFuture : std::false_type {
-  typedef T Inner;
+  using Inner = typename Unit::Lift<T>::type;
 };
 
 template <typename T>
@@ -63,7 +63,7 @@ struct ArgType<> {
 
 template <bool isTry, typename F, typename... Args>
 struct argResult {
-  typedef resultOf<F, Args...> Result;
+  using Result = resultOf<F, Args...>;
 };
 
 template<typename F, typename... Args>
@@ -100,19 +100,6 @@ struct callableResult {
   typedef Future<typename ReturnsFuture::Inner> Return;
 };
 
-template<typename F>
-struct callableResult<void, F> {
-  typedef typename std::conditional<
-    callableWith<F>::value,
-    detail::argResult<false, F>,
-    typename std::conditional<
-      callableWith<F, Try<void>&&>::value,
-      detail::argResult<true, F, Try<void>&&>,
-      detail::argResult<true, F, Try<void>&>>::type>::type Arg;
-  typedef isFuture<typename Arg::Result> ReturnsFuture;
-  typedef Future<typename ReturnsFuture::Inner> Return;
-};
-
 template <typename L>
 struct Extract : Extract<decltype(&L::operator())> { };
 
index 78f33d2..e4265eb 100644 (file)
@@ -20,7 +20,7 @@
 namespace folly {
 
 // Instantiate the most common Future types to save compile time
-template class Future<void>;
+template class Future<Unit>;
 template class Future<bool>;
 template class Future<int>;
 template class Future<int64_t>;
@@ -31,7 +31,7 @@ template class Future<double>;
 
 namespace folly { namespace futures {
 
-Future<void> sleep(Duration dur, Timekeeper* tk) {
+Future<Unit> sleep(Duration dur, Timekeeper* tk) {
   if (LIKELY(!tk)) {
     tk = detail::getTimekeeperSingleton();
   }
@@ -39,19 +39,3 @@ Future<void> sleep(Duration dur, Timekeeper* tk) {
 }
 
 }}
-
-namespace folly { namespace detail {
-
-template <>
-CollectContext<void>::~CollectContext() {
-  if (!threw.exchange(true)) {
-    p.setValue();
-  }
-}
-
-template <>
-void CollectContext<void>::setPartialResult(size_t i, Try<void>& t) {
-  // Nothing to do for void
-}
-
-}}
index 649cd66..a1f7826 100644 (file)
@@ -62,7 +62,7 @@ class Future {
 
   template <class T2 = T, typename =
             typename std::enable_if<
-              folly::is_void_or_unit<T2>::value>::type>
+              std::is_same<Unit, T2>::value>::type>
   Future();
 
   ~Future();
@@ -202,9 +202,9 @@ class Future {
     -> decltype(this->then(std::forward<Arg>(arg),
                            std::forward<Args>(args)...));
 
-  /// Convenience method for ignoring the value and creating a Future<void>.
+  /// Convenience method for ignoring the value and creating a Future<Unit>.
   /// Exceptions still propagate.
-  Future<void> then();
+  Future<Unit> then();
 
   /// Set an error callback for this Future. The callback should take a single
   /// argument of the type that you want to catch, and should return a value of
index c6af916..88f6631 100644 (file)
@@ -70,13 +70,6 @@ public:
   /// handled.
   void setInterruptHandler(std::function<void(exception_wrapper const&)>);
 
-  /// Fulfill this Promise<void>
-  template <class B = T>
-  typename std::enable_if<std::is_void<B>::value, void>::type
-  setValue() {
-    setTry(Try<T>());
-  }
-
   /// Sugar to fulfill this Promise<Unit>
   template <class B = T>
   typename std::enable_if<std::is_same<Unit, B>::value, void>::type
index 0c8fcd9..1d5670c 100644 (file)
@@ -81,13 +81,6 @@ public:
   /// handled.
   void setInterruptHandler(std::function<void(exception_wrapper const&)>);
 
-  /// Fulfill this SharedPromise<void>
-  template <class B = T>
-  typename std::enable_if<std::is_void<B>::value, void>::type
-  setValue() {
-    setTry(Try<T>());
-  }
-
   /// Sugar to fulfill this SharedPromise<Unit>
   template <class B = T>
   typename std::enable_if<std::is_same<Unit, B>::value, void>::type
index 3612f5c..3e3e20a 100644 (file)
 #pragma once
 
 #include <folly/futures/detail/Types.h>
+#include <folly/futures/Unit.h>
 
 namespace folly {
 
 template <class> class Future;
 
 /// A Timekeeper handles the details of keeping time and fulfilling delay
-/// promises. The returned Future<void> will either complete after the
+/// promises. The returned Future<Unit> will either complete after the
 /// elapsed time, or in the event of some kind of exceptional error may hold
 /// an exception. These Futures respond to cancellation. If you use a lot of
 /// Delays and many of them ultimately are unneeded (as would be the case for
@@ -34,7 +35,7 @@ template <class> class Future;
 /// use them implicitly behind the scenes by passing a timeout to some Future
 /// operation.
 ///
-/// Although we don't formally alias Delay = Future<void>,
+/// Although we don't formally alias Delay = Future<Unit>,
 /// that's an appropriate term for it. People will probably also call these
 /// Timeouts, and that's ok I guess, but that term is so overloaded I thought
 /// it made sense to introduce a cleaner term.
@@ -54,7 +55,7 @@ class Timekeeper {
   /// This future probably completes on the timer thread. You should almost
   /// certainly follow it with a via() call or the accuracy of other timers
   /// will suffer.
-  virtual Future<void> after(Duration) = 0;
+  virtual Future<Unit> after(Duration) = 0;
 
   /// Returns a future that will complete at the requested time.
   ///
@@ -65,7 +66,7 @@ class Timekeeper {
   /// the system clock but rather execute that many milliseconds in the future
   /// according to the steady clock.
   template <class Clock>
-  Future<void> at(std::chrono::time_point<Clock> when);
+  Future<Unit> at(std::chrono::time_point<Clock> when);
 };
 
 } // namespace folly
@@ -77,7 +78,7 @@ class Timekeeper {
 namespace folly {
 
 template <class Clock>
-Future<void> Timekeeper::at(std::chrono::time_point<Clock> when) {
+Future<Unit> Timekeeper::at(std::chrono::time_point<Clock> when) {
   auto now = Clock::now();
 
   if (when <= now) {
index fac9cc5..05f384a 100644 (file)
@@ -31,6 +31,21 @@ Try<T>::Try(Try<T>&& t) noexcept : contains_(t.contains_) {
   }
 }
 
+template <class T>
+template <class T2>
+Try<T>::Try(typename std::enable_if<std::is_same<Unit, T2>::value,
+                                    Try<void> const&>::type t)
+    : contains_(Contains::NOTHING) {
+  if (t.hasValue()) {
+    contains_ = Contains::VALUE;
+    new (&value_) T();
+  } else if (t.hasException()) {
+    contains_ = Contains::EXCEPTION;
+    new (&e_) std::unique_ptr<exception_wrapper>(
+        folly::make_unique<exception_wrapper>(t.exception()));
+  }
+}
+
 template <class T>
 Try<T>& Try<T>::operator=(Try<T>&& t) noexcept {
   if (this == &t) {
index 5f5f634..d06d5b6 100644 (file)
@@ -35,8 +35,7 @@ namespace folly {
  * context. Exceptions are stored as exception_wrappers so that the user can
  * minimize rethrows if so desired.
  *
- * There is a specialization, Try<void>, which represents either success
- * or an exception.
+ * To represent success or a captured exception, use Try<Unit>
  */
 template <class T>
 class Try {
@@ -74,6 +73,12 @@ class Try {
    */
   explicit Try(T&& v) : contains_(Contains::VALUE), value_(std::move(v)) {}
 
+  /// Implicit conversion from Try<void> to Try<Unit>
+  template <class T2 = T>
+  /* implicit */
+  Try(typename std::enable_if<std::is_same<Unit, T2>::value,
+                              Try<void> const&>::type t);
+
   /*
    * Construct a Try with an exception_wrapper
    *
@@ -364,7 +369,7 @@ typename std::enable_if<
 makeTryWith(F&& f);
 
 /*
- * Specialization of makeTryWith for void
+ * Specialization of makeTryWith for void return
  *
  * @param f a function to execute and capture the result of
  *
index 44c3661..d4d99d6 100644 (file)
@@ -22,8 +22,6 @@ namespace folly {
 /// metaprogramming. So, instead of e.g. Future<void>, we have Future<Unit>.
 /// You can ignore the actual value, and we port some of the syntactic
 /// niceties like setValue() instead of setValue(Unit{}).
-// We will soon return Future<Unit> wherever we currently return Future<void>
-// #6847876
 struct Unit {
   /// Lift type T into Unit. This is the definition for all non-void types.
   template <class T> struct Lift : public std::false_type {
index 8b5745d..cd10671 100644 (file)
@@ -32,13 +32,13 @@ namespace {
       return new WTCallback(base);
     }
 
-    Future<void> getFuture() {
+    Future<Unit> getFuture() {
       return promise_.getFuture();
     }
 
    protected:
     EventBase* base_;
-    Promise<void> promise_;
+    Promise<Unit> promise_;
 
     explicit WTCallback(EventBase* base)
         : base_(base) {
@@ -81,7 +81,7 @@ ThreadWheelTimekeeper::~ThreadWheelTimekeeper() {
   thread_.join();
 }
 
-Future<void> ThreadWheelTimekeeper::after(Duration dur) {
+Future<Unit> ThreadWheelTimekeeper::after(Duration dur) {
   auto cob = WTCallback::create(&eventBase_);
   auto f = cob->getFuture();
   eventBase_.runInEventBaseThread([=]{
index ddbb442..9625de7 100644 (file)
@@ -37,7 +37,7 @@ class ThreadWheelTimekeeper : public Timekeeper {
   /// This future *does* complete on the timer thread. You should almost
   /// certainly follow it with a via() call or the accuracy of other timers
   /// will suffer.
-  Future<void> after(Duration) override;
+  Future<Unit> after(Duration) override;
 
  protected:
   folly::EventBase eventBase_;
index c099f58..0d0b881 100644 (file)
@@ -37,7 +37,7 @@ namespace futures {
   /// The Timekeeper thread will be lazily created the first time it is
   /// needed. If your program never uses any timeouts or other time-based
   /// Futures you will pay no Timekeeper thread overhead.
-  Future<void> sleep(Duration, Timekeeper* = nullptr);
+  Future<Unit> sleep(Duration, Timekeeper* = nullptr);
 
   /**
    * Set func as the callback for each input Future and return a vector of
@@ -72,21 +72,19 @@ template <class T>
 Future<typename std::decay<T>::type> makeFuture(T&& t);
 
 /** Make a completed void Future. */
-Future<void> makeFuture();
+Future<Unit> makeFuture();
 
 /** Make a completed Future by executing a function. If the function throws
   we capture the exception, otherwise we capture the result. */
 template <class F>
 auto makeFutureWith(
-  F&& func,
-  typename std::enable_if<
-    !std::is_reference<F>::value, bool>::type sdf = false)
-  -> Future<decltype(func())>;
+    F&& func,
+    typename std::enable_if<!std::is_reference<F>::value, bool>::type sdf)
+    -> Future<typename Unit::Lift<decltype(func())>::type>;
 
 template <class F>
-auto makeFutureWith(
-  F const& func)
-  -> Future<decltype(func())>;
+auto makeFutureWith(F const& func)
+    -> Future<typename Unit::Lift<decltype(func())>::type>;
 
 /// Make a failed Future from an exception_ptr.
 /// Because the Future's type cannot be inferred you have to specify it, e.g.
@@ -120,7 +118,7 @@ Future<T> makeFuture(Try<T>&& t);
  *
  * @returns a void Future that will call back on the given executor
  */
-inline Future<void> via(
+inline Future<Unit> via(
     Executor* executor,
     int8_t priority = Executor::MID_PRI);
 
index 8721622..6151832 100644 (file)
@@ -170,8 +170,8 @@ BENCHMARK_DRAW_LINE();
 // The old way. Throw an exception, and rethrow to access it upstream.
 void throwAndCatchImpl() {
   makeFuture()
-      .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
-      .then([](Try<void>&& t) {
+      .then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
+      .then([](Try<Unit>&& t) {
         try {
           t.value();
         } catch(const std::runtime_error& e) {
@@ -190,8 +190,8 @@ void throwAndCatchImpl() {
 // will try to wrap, so no exception_ptrs/rethrows are necessary.
 void throwAndCatchWrappedImpl() {
   makeFuture()
-      .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
-      .then([](Try<void>&& t) {
+      .then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
+      .then([](Try<Unit>&& t) {
         auto caught = t.withException<std::runtime_error>(
             [](const std::runtime_error& e){
               // ...
@@ -203,10 +203,10 @@ void throwAndCatchWrappedImpl() {
 // Better. Wrap an exception, and rethrow to access it upstream.
 void throwWrappedAndCatchImpl() {
   makeFuture()
-      .then([](Try<void>&&){
-        return makeFuture<void>(std::runtime_error("oh no"));
+      .then([](Try<Unit>&&){
+        return makeFuture<Unit>(std::runtime_error("oh no"));
       })
-      .then([](Try<void>&& t) {
+      .then([](Try<Unit>&& t) {
         try {
           t.value();
         } catch(const std::runtime_error& e) {
@@ -220,10 +220,10 @@ void throwWrappedAndCatchImpl() {
 // The new way. Wrap an exception, and access it via the wrapper upstream
 void throwWrappedAndCatchWrappedImpl() {
   makeFuture()
-      .then([](Try<void>&&){
-        return makeFuture<void>(std::runtime_error("oh no"));
+      .then([](Try<Unit>&&){
+        return makeFuture<Unit>(std::runtime_error("oh no"));
       })
-      .then([](Try<void>&& t){
+      .then([](Try<Unit>&& t){
         auto caught = t.withException<std::runtime_error>(
             [](const std::runtime_error& e){
               // ...
index f9746d1..5834f22 100644 (file)
@@ -87,14 +87,14 @@ TEST(Collect, collectAll) {
 
   // check that futures are ready in then()
   {
-    std::vector<Promise<void>> promises(10);
-    std::vector<Future<void>> futures;
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
 
     for (auto& p : promises)
       futures.push_back(p.getFuture());
 
     auto allf = collectAll(futures)
-      .then([](Try<std::vector<Try<void>>>&& ts) {
+      .then([](Try<std::vector<Try<Unit>>>&& ts) {
         for (auto& f : ts.value())
           f.value();
       });
@@ -166,8 +166,8 @@ TEST(Collect, collect) {
 
   // void futures success case
   {
-    std::vector<Promise<void>> promises(10);
-    std::vector<Future<void>> futures;
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
 
     for (auto& p : promises)
       futures.push_back(p.getFuture());
@@ -185,8 +185,8 @@ TEST(Collect, collect) {
 
   // void futures failure case
   {
-    std::vector<Promise<void>> promises(10);
-    std::vector<Future<void>> futures;
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
 
     for (auto& p : promises)
       futures.push_back(p.getFuture());
@@ -294,8 +294,8 @@ TEST(Collect, collectAny) {
 
   // error
   {
-    std::vector<Promise<void>> promises(10);
-    std::vector<Future<void>> futures;
+    std::vector<Promise<Unit>> promises(10);
+    std::vector<Future<Unit>> futures;
 
     for (auto& p : promises)
       futures.push_back(p.getFuture());
@@ -334,12 +334,12 @@ TEST(Collect, collectAny) {
 
 TEST(Collect, alreadyCompleted) {
   {
-    std::vector<Future<void>> fs;
+    std::vector<Future<Unit>> fs;
     for (int i = 0; i < 10; i++)
       fs.push_back(makeFuture());
 
     collectAll(fs)
-      .then([&](std::vector<Try<void>> ts) {
+      .then([&](std::vector<Try<Unit>> ts) {
         EXPECT_EQ(fs.size(), ts.size());
       });
   }
@@ -484,8 +484,8 @@ TEST(Collect, allParallelWithError) {
 }
 
 TEST(Collect, collectN) {
-  std::vector<Promise<void>> promises(10);
-  std::vector<Future<void>> futures;
+  std::vector<Promise<Unit>> promises(10);
+  std::vector<Future<Unit>> futures;
 
   for (auto& p : promises)
     futures.push_back(p.getFuture());
@@ -493,7 +493,7 @@ TEST(Collect, collectN) {
   bool flag = false;
   size_t n = 3;
   collectN(futures, n)
-    .then([&](std::vector<std::pair<size_t, Try<void>>> v) {
+    .then([&](std::vector<std::pair<size_t, Try<Unit>>> v) {
       flag = true;
       EXPECT_EQ(n, v.size());
       for (auto& tt : v)
@@ -510,13 +510,13 @@ TEST(Collect, collectN) {
 
 /// Ensure that we can compile collectAll/Any with folly::small_vector
 TEST(Collect, smallVector) {
-  static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<void>),
+  static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<Unit>),
                 "Futures should not be trivially copyable");
   static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future<int>),
                 "Futures should not be trivially copyable");
 
   {
-    folly::small_vector<Future<void>> futures;
+    folly::small_vector<Future<Unit>> futures;
 
     for (int i = 0; i < 10; i++)
       futures.push_back(makeFuture());
@@ -524,7 +524,7 @@ TEST(Collect, smallVector) {
     auto anyf = collectAny(futures);
   }
   {
-    folly::small_vector<Future<void>> futures;
+    folly::small_vector<Future<Unit>> futures;
 
     for (int i = 0; i < 10; i++)
       futures.push_back(makeFuture());
index 487753a..1e09a3a 100644 (file)
@@ -40,7 +40,7 @@ TEST(Context, basic) {
     std::unique_ptr<TestData>(new TestData(10)));
 
   // Start a future
-  Promise<void> p;
+  Promise<Unit> p;
   auto future = p.getFuture().then([&]{
     // Check that the context followed the future
     EXPECT_TRUE(RequestContext::get() != nullptr);
index 7e2ad64..dedfeec 100644 (file)
@@ -24,5 +24,5 @@ using namespace folly;
 TEST(Core, size) {
   // If this number goes down, it's fine!
   // If it goes up, please seek professional advice ;-)
-  EXPECT_EQ(192, sizeof(detail::Core<void>));
+  EXPECT_EQ(192, sizeof(detail::Core<Unit>));
 }
index c7b06dd..0f0fe18 100644 (file)
@@ -95,7 +95,7 @@ TEST(ManualExecutor, waitForDoesNotDeadlock) {
   folly::Baton<> baton;
   auto f = makeFuture()
     .via(&east)
-    .then([](Try<void>){ return makeFuture(); })
+    .then([](Try<Unit>){ return makeFuture(); })
     .via(&west);
   std::thread t([&]{
     baton.post();
@@ -162,7 +162,7 @@ TEST(Executor, RunnablePtr) {
 
 TEST(Executor, ThrowableThen) {
   InlineExecutor x;
-  auto f = Future<void>().via(&x).then([](){
+  auto f = Future<Unit>().via(&x).then([](){
     throw std::runtime_error("Faildog");
   });
   EXPECT_THROW(f.value(), std::exception);
index d6e1990..16c9f1f 100644 (file)
@@ -320,11 +320,11 @@ TEST(Future, thenTry) {
     .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
   EXPECT_TRUE(flag); flag = false;
 
-  makeFuture().then([&](Try<void>&& t) { flag = true; t.value(); });
+  makeFuture().then([&](Try<Unit>&& t) { flag = true; t.value(); });
   EXPECT_TRUE(flag); flag = false;
 
-  Promise<void> p;
-  auto f = p.getFuture().then([&](Try<void>&& t) { flag = true; });
+  Promise<Unit> p;
+  auto f = p.getFuture().then([&](Try<Unit>&& t) { flag = true; });
   EXPECT_FALSE(flag);
   EXPECT_FALSE(f.isReady());
   p.setValue();
@@ -353,7 +353,7 @@ TEST(Future, thenValue) {
   auto f = makeFuture<int>(eggs).then([&](int i){});
   EXPECT_THROW(f.value(), eggs_t);
 
-  f = makeFuture<void>(eggs).then([&]{});
+  f = makeFuture<Unit>(eggs).then([&]{});
   EXPECT_THROW(f.value(), eggs_t);
 }
 
@@ -366,7 +366,7 @@ TEST(Future, thenValueFuture) {
 
   makeFuture()
     .then([]{ return makeFuture(); })
-    .then([&](Try<void>&& t) { flag = true; });
+    .then([&](Try<Unit>&& t) { flag = true; });
   EXPECT_TRUE(flag); flag = false;
 }
 
@@ -505,7 +505,7 @@ TEST(Future, makeFuture) {
   EXPECT_TYPE(makeFutureWith(failfun), Future<int>);
   EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t);
 
-  EXPECT_TYPE(makeFuture(), Future<void>);
+  EXPECT_TYPE(makeFuture(), Future<Unit>);
 }
 
 TEST(Future, finish) {
@@ -568,18 +568,18 @@ TEST(Future, unwrap) {
 TEST(Future, throwCaughtInImmediateThen) {
   // Neither of these should throw "Promise already satisfied"
   makeFuture().then(
-    [=](Try<void>&&) -> int { throw std::exception(); });
+    [=](Try<Unit>&&) -> int { throw std::exception(); });
   makeFuture().then(
-    [=](Try<void>&&) -> Future<int> { throw std::exception(); });
+    [=](Try<Unit>&&) -> Future<int> { throw std::exception(); });
 }
 
 TEST(Future, throwIfFailed) {
-  makeFuture<void>(eggs)
-    .then([=](Try<void>&& t) {
+  makeFuture<Unit>(eggs)
+    .then([=](Try<Unit>&& t) {
       EXPECT_THROW(t.throwIfFailed(), eggs_t);
     });
   makeFuture()
-    .then([=](Try<void>&& t) {
+    .then([=](Try<Unit>&& t) {
       EXPECT_NO_THROW(t.throwIfFailed());
     });
 
@@ -600,7 +600,7 @@ TEST(Future, getFutureAfterSetValue) {
 }
 
 TEST(Future, getFutureAfterSetException) {
-  Promise<void> p;
+  Promise<Unit> p;
   p.setWith([]() -> void { throw std::logic_error("foo"); });
   EXPECT_THROW(p.getFuture().value(), std::logic_error);
 }
@@ -655,7 +655,7 @@ TEST(Future, CircularDependencySharedPtrSelfReset) {
 TEST(Future, Constructor) {
   auto f1 = []() -> Future<int> { return Future<int>(3); }();
   EXPECT_EQ(f1.value(), 3);
-  auto f2 = []() -> Future<void> { return Future<void>(); }();
+  auto f2 = []() -> Future<Unit> { return Future<Unit>(); }();
   EXPECT_NO_THROW(f2.value());
 }
 
@@ -664,7 +664,7 @@ TEST(Future, ImplicitConstructor) {
   EXPECT_EQ(f1.value(), 3);
   // Unfortunately, the C++ standard does not allow the
   // following implicit conversion to work:
-  //auto f2 = []() -> Future<void> { }();
+  //auto f2 = []() -> Future<Unit> { }();
 }
 
 TEST(Future, thenDynamic) {
@@ -734,3 +734,7 @@ TEST(Future, RequestContext) {
   p1.setValue(3);
   p2.setValue(4);
 }
+
+TEST(Future, makeFutureNoThrow) {
+  makeFuture().value();
+}
index 1963c0a..99f8b69 100644 (file)
@@ -23,7 +23,7 @@ using namespace folly;
 
 TEST(Interrupt, raise) {
   std::runtime_error eggs("eggs");
-  Promise<void> p;
+  Promise<Unit> p;
   p.setInterruptHandler([&](const exception_wrapper& e) {
     EXPECT_THROW(e.throwException(), decltype(eggs));
   });
@@ -31,7 +31,7 @@ TEST(Interrupt, raise) {
 }
 
 TEST(Interrupt, cancel) {
-  Promise<void> p;
+  Promise<Unit> p;
   p.setInterruptHandler([&](const exception_wrapper& e) {
     EXPECT_THROW(e.throwException(), FutureCancellation);
   });
@@ -55,7 +55,7 @@ TEST(Interrupt, interruptThenHandle) {
 }
 
 TEST(Interrupt, interruptAfterFulfilNoop) {
-  Promise<void> p;
+  Promise<Unit> p;
   bool flag = false;
   p.setInterruptHandler([&](const exception_wrapper& e) { flag = true; });
   p.setValue();
@@ -64,7 +64,7 @@ TEST(Interrupt, interruptAfterFulfilNoop) {
 }
 
 TEST(Interrupt, secondInterruptNoop) {
-  Promise<void> p;
+  Promise<Unit> p;
   int count = 0;
   p.setInterruptHandler([&](const exception_wrapper& e) { count++; });
   auto f = p.getFuture();
index ed0313d..ec75bf8 100644 (file)
@@ -31,7 +31,7 @@ TEST(Map, basic) {
   fs.push_back(p3.getFuture());
 
   int c = 0;
-  std::vector<Future<void>> fs2 = futures::map(fs, [&](int i){
+  std::vector<Future<Unit>> fs2 = futures::map(fs, [&](int i){
     c += i;
   });
 
index 166dea6..d241b4e 100644 (file)
@@ -34,7 +34,7 @@ TEST(Poll, notReady) {
 }
 
 TEST(Poll, exception) {
-  Promise<void> p;
+  Promise<Unit> p;
   auto f = p.getFuture();
   p.setWith([] { throw std::runtime_error("Runtime"); });
   EXPECT_TRUE(f.poll().value().hasException());
index 5e20706..6338d68 100644 (file)
@@ -70,7 +70,7 @@ TEST(Promise, setValue) {
   unique_ptr<int> ptr = std::move(fmov.value());
   EXPECT_EQ(42, *ptr);
 
-  Promise<void> v;
+  Promise<Unit> v;
   auto fv = v.getFuture();
   v.setValue();
   EXPECT_TRUE(fv.isReady());
@@ -78,13 +78,13 @@ TEST(Promise, setValue) {
 
 TEST(Promise, setException) {
   {
-    Promise<void> p;
+    Promise<Unit> p;
     auto f = p.getFuture();
     p.setException(eggs);
     EXPECT_THROW(f.value(), eggs_t);
   }
   {
-    Promise<void> p;
+    Promise<Unit> p;
     auto f = p.getFuture();
     try {
       throw eggs;
index d913b70..4c500cb 100644 (file)
@@ -123,7 +123,7 @@ TEST(Timekeeper, futureWithinVoidSpecialization) {
 }
 
 TEST(Timekeeper, futureWithinException) {
-  Promise<void> p;
+  Promise<Unit> p;
   auto f = p.getFuture().within(awhile, std::runtime_error("expected"));
   EXPECT_THROW(f.get(), std::runtime_error);
 }
@@ -150,7 +150,7 @@ TEST(Timekeeper, onTimeoutVoid) {
      });
   makeFuture().delayed(one_ms)
     .onTimeout(Duration(0), [&]{
-       return makeFuture<void>(std::runtime_error("expected"));
+       return makeFuture<Unit>(std::runtime_error("expected"));
      });
   // just testing compilation here
 }
index 479fcb9..cc111c1 100644 (file)
@@ -36,7 +36,7 @@ TEST(Try, basic) {
   A a(5);
   Try<A> t_a(std::move(a));
 
-  Try<void> t_void;
+  Try<Unit> t_void;
 
   EXPECT_EQ(5, t_a.value().x());
 }
index 7e09a10..e4ef1eb 100644 (file)
@@ -35,7 +35,7 @@ TEST(Unit, operatorNe) {
 }
 
 TEST(Unit, voidOrUnit) {
-  EXPECT_TRUE(is_void_or_unit<void>::value);
+  EXPECT_TRUE(is_void_or_unit<Unit>::value);
   EXPECT_TRUE(is_void_or_unit<Unit>::value);
   EXPECT_FALSE(is_void_or_unit<int>::value);
 }
@@ -53,7 +53,7 @@ TEST(Unit, liftInt) {
 }
 
 TEST(Unit, liftVoid) {
-  using Lifted = Unit::Lift<void>;
+  using Lifted = Unit::Lift<Unit>;
   EXPECT_TRUE(Lifted::value);
   auto v = std::is_same<Unit, Lifted::type>::value;
   EXPECT_TRUE(v);
@@ -68,7 +68,7 @@ TEST(Unit, futureToUnit) {
 TEST(Unit, voidFutureToUnit) {
   Future<Unit> fu = makeFuture().unit();
   fu.value();
-  EXPECT_TRUE(makeFuture<void>(eggs).unit().hasException());
+  EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
 }
 
 TEST(Unit, unitFutureToUnitIdentity) {
@@ -84,3 +84,9 @@ TEST(Unit, toUnitWhileInProgress) {
   p.setValue(42);
   EXPECT_TRUE(fu.isReady());
 }
+
+TEST(Unit, makeFutureWith) {
+  int count = 0;
+  Future<Unit> fu = makeFutureWith([&]{ count++; });
+  EXPECT_EQ(1, count);
+}
index 5027ed9..6408ee3 100644 (file)
@@ -124,7 +124,7 @@ TEST(Via, thenFunction) {
 
 TEST_F(ViaFixture, threadHops) {
   auto westThreadId = std::this_thread::get_id();
-  auto f = via(eastExecutor.get()).then([=](Try<void>&& t) {
+  auto f = via(eastExecutor.get()).then([=](Try<Unit>&& t) {
     EXPECT_NE(std::this_thread::get_id(), westThreadId);
     return makeFuture<int>(1);
   }).via(westExecutor.get()
@@ -283,7 +283,7 @@ TEST(Via, then2) {
 }
 
 TEST(Via, then2Variadic) {
-  struct Foo { bool a = false; void foo(Try<void>) { a = true; } };
+  struct Foo { bool a = false; void foo(Try<Unit>) { a = true; } };
   Foo f;
   ManualExecutor x;
   makeFuture().then(&x, &Foo::foo, &f);
@@ -345,14 +345,14 @@ TEST(Via, callbackRace) {
   ThreadExecutor x;
 
   auto fn = [&x]{
-    auto promises = std::make_shared<std::vector<Promise<void>>>(4);
-    std::vector<Future<void>> futures;
+    auto promises = std::make_shared<std::vector<Promise<Unit>>>(4);
+    std::vector<Future<Unit>> futures;
 
     for (auto& p : *promises) {
       futures.emplace_back(
         p.getFuture()
         .via(&x)
-        .then([](Try<void>&&){}));
+        .then([](Try<Unit>&&){}));
     }
 
     x.waitForStartup();
@@ -424,16 +424,16 @@ TEST(Via, waitVia) {
 
 TEST(Via, viaRaces) {
   ManualExecutor x;
-  Promise<void> p;
+  Promise<Unit> p;
   auto tid = std::this_thread::get_id();
   bool done = false;
 
   std::thread t1([&] {
     p.getFuture()
       .via(&x)
-      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
-      .then([&](Try<void>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
-      .then([&](Try<void>&&) { done = true; });
+      .then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<Unit>&&) { EXPECT_EQ(tid, std::this_thread::get_id()); })
+      .then([&](Try<Unit>&&) { done = true; });
   });
 
   std::thread t2([&] {
@@ -448,7 +448,7 @@ TEST(Via, viaRaces) {
 TEST(ViaFunc, liftsVoid) {
   ManualExecutor x;
   int count = 0;
-  Future<void> f = via(&x, [&]{ count++; });
+  Future<Unit> f = via(&x, [&]{ count++; });
 
   EXPECT_EQ(0, count);
   x.run();
index d2b91f9..b2736f1 100644 (file)
@@ -34,7 +34,7 @@ TEST(Wait, waitImmediate) {
   EXPECT_EQ(v.size(), done_v.size());
   EXPECT_EQ(v, done_v);
 
-  vector<Future<void>> v_f;
+  vector<Future<Unit>> v_f;
   v_f.push_back(makeFuture());
   v_f.push_back(makeFuture());
   auto done_v_f = collectAll(v_f).wait().value();
@@ -169,7 +169,7 @@ TEST(Wait, waitWithDuration) {
  }
 
  {
-   Promise<void> p;
+   Promise<Unit> p;
    auto start = std::chrono::steady_clock::now();
    auto f = p.getFuture().wait(milliseconds(100));
    auto elapsed = std::chrono::steady_clock::now() - start;
@@ -182,7 +182,7 @@ TEST(Wait, waitWithDuration) {
  {
    // Try to trigger the race where the resultant Future is not yet complete
    // even if we didn't hit the timeout, and make sure we deal with it properly
-   Promise<void> p;
+   Promise<Unit> p;
    folly::Baton<> b;
    auto t = std::thread([&]{
      b.post();
index 9dbdb15..a7e9b82 100644 (file)
@@ -57,14 +57,14 @@ TEST(Window, basic) {
     fn(input, 1, 0);
   }
   {
-    // int -> Future<void>
+    // int -> Future<Unit>
     auto res = reduce(
       window(
         std::vector<int>({1, 2, 3}),
         [](int i) { return makeFuture(); },
         2),
       0,
-      [](int sum, const Try<void>& b) {
+      [](int sum, const Try<Unit>& b) {
         EXPECT_TRUE(b.hasValue());
         return sum + 1;
       }).get();