folly::via(Executor*, Func)
authorHans Fugal <fugalh@fb.com>
Thu, 11 Jun 2015 18:51:06 +0000 (11:51 -0700)
committerSara Golemon <sgolemon@fb.com>
Thu, 11 Jun 2015 20:15:13 +0000 (13:15 -0700)
Summary: ((not yet) more performant) sugar for `via(&x).then(func)`

Reviewed By: @​hannesr

Differential Revision: D2131246

folly/futures/Future-inl.h
folly/futures/helpers.h
folly/futures/test/ViaTest.cpp

index 0b2a301c5014c134f5d5ebed226971df79d7aa7e..59498af986ef9768a745b502e7968af11e32a555 100644 (file)
@@ -435,6 +435,17 @@ inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
   return std::move(f).via(executor, priority);
 }
 
   return std::move(f).via(executor, 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);
+}
+
 template <class T>
 bool Future<T>::isReady() const {
   throwIfInvalid();
 template <class T>
 bool Future<T>::isReady() const {
   throwIfInvalid();
index 5ae28f1f4f41a092c34d981591a4fe2ef79f80be..c099f580b864d094b8b0ad0d2d945728e82c6740 100644 (file)
@@ -56,7 +56,7 @@ namespace futures {
     return map(c.begin(), c.end(), std::forward<F>(func));
   }
 
     return map(c.begin(), c.end(), std::forward<F>(func));
   }
 
-}
+} // namespace futures
 
 /**
   Make a completed Future by moving in a value. e.g.
 
 /**
   Make a completed Future by moving in a value. e.g.
@@ -124,6 +124,13 @@ inline Future<void> via(
     Executor* executor,
     int8_t priority = Executor::MID_PRI);
 
     Executor* executor,
     int8_t priority = Executor::MID_PRI);
 
+/// Execute a function via the given executor and return a future.
+/// This is semantically equivalent to via(executor).then(func), but
+/// easier to read and slightly more efficient.
+template <class Func>
+auto via(Executor*, Func func)
+  -> Future<typename isFuture<decltype(func())>::Inner>;
+
 /** When all the input Futures complete, the returned Future will complete.
   Errors do not cause early termination; this Future will always succeed
   after all its Futures have finished (whether successfully or with an
 /** When all the input Futures complete, the returned Future will complete.
   Errors do not cause early termination; this Future will always succeed
   after all its Futures have finished (whether successfully or with an
@@ -286,4 +293,4 @@ auto unorderedReduce(Collection&& c, T&& initial, F&& func)
       std::forward<F>(func));
 }
 
       std::forward<F>(func));
 }
 
-} // namespace folly
+} // namespace
index 72b2e1e86f636ac67b143ae72d1b2faf91743840..499d61d4159ae3b7c14e3d89336f514ef73bdc5c 100644 (file)
@@ -415,7 +415,7 @@ TEST(Via, viaRaces) {
   t2.join();
 }
 
   t2.join();
 }
 
-TEST(Future, callbackRace) {
+TEST(Via, callbackRace) {
   ThreadExecutor x;
 
   auto fn = [&x]{
   ThreadExecutor x;
 
   auto fn = [&x]{
@@ -441,3 +441,52 @@ TEST(Future, callbackRace) {
 
   fn().wait();
 }
 
   fn().wait();
 }
+
+TEST(ViaFunc, liftsVoid) {
+  ManualExecutor x;
+  int count = 0;
+  Future<void> f = via(&x, [&]{ count++; });
+
+  EXPECT_EQ(0, count);
+  x.run();
+  EXPECT_EQ(1, count);
+}
+
+TEST(ViaFunc, value) {
+  ManualExecutor x;
+  EXPECT_EQ(42, via(&x, []{ return 42; }).getVia(&x));
+}
+
+TEST(ViaFunc, exception) {
+  ManualExecutor x;
+  EXPECT_THROW(
+    via(&x, []() -> int { throw std::runtime_error("expected"); })
+      .getVia(&x),
+    std::runtime_error);
+}
+
+TEST(ViaFunc, future) {
+  ManualExecutor x;
+  EXPECT_EQ(42, via(&x, []{ return makeFuture(42); })
+            .getVia(&x));
+}
+
+TEST(ViaFunc, voidFuture) {
+  ManualExecutor x;
+  int count = 0;
+  via(&x, [&]{ count++; }).getVia(&x);
+  EXPECT_EQ(1, count);
+}
+
+TEST(ViaFunc, isSticky) {
+  ManualExecutor x;
+  int count = 0;
+
+  auto f = via(&x, [&]{ count++; });
+  x.run();
+
+  f.then([&]{ count++; });
+  EXPECT_EQ(1, count);
+  x.run();
+  EXPECT_EQ(2, count);
+}