Ensure that returning a semifuture from a continuation works correctly.
authorLee Howes <lwh@fb.com>
Tue, 26 Dec 2017 21:34:26 +0000 (13:34 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Tue, 26 Dec 2017 21:35:23 +0000 (13:35 -0800)
Summary: Returning a SemiFuture from a continuation should work by correctly checking the types and returning a folly::Future on the same executor as the original future that .then was applied to.

Reviewed By: yfeldblum

Differential Revision: D6597273

fbshipit-source-id: cf2016a344d4b29f1d31c1da20c89df5b4cfe64e

folly/futures/Future-pre.h
folly/futures/test/SemiFutureTest.cpp

index 82a8684cbfc468ae512a4857b1c4fc6b2ba4189f..560b4df6a642b9bfe4516849e61afeb310d06915 100644 (file)
@@ -45,6 +45,24 @@ struct isFuture<Future<T>> : std::true_type {
   typedef T Inner;
 };
 
+template <typename T>
+struct isFutureOrSemiFuture : std::false_type {
+  using Inner = typename Unit::Lift<T>::type;
+  using Return = Inner;
+};
+
+template <typename T>
+struct isFutureOrSemiFuture<Future<T>> : std::true_type {
+  typedef T Inner;
+  using Return = Future<Inner>;
+};
+
+template <typename T>
+struct isFutureOrSemiFuture<SemiFuture<T>> : std::true_type {
+  typedef T Inner;
+  using Return = SemiFuture<Inner>;
+};
+
 template <typename T>
 struct isTry : std::false_type {};
 
@@ -109,7 +127,7 @@ struct callableResult {
           callableWith<F, Try<T>&&>::value,
           detail::argResult<true, F, Try<T>&&>,
           detail::argResult<true, F, Try<T>&>>::type>::type>::type>::type Arg;
-  typedef isFuture<typename Arg::Result> ReturnsFuture;
+  typedef isFutureOrSemiFuture<typename Arg::Result> ReturnsFuture;
   typedef Future<typename ReturnsFuture::Inner> Return;
 };
 
@@ -118,7 +136,7 @@ struct Extract : Extract<decltype(&L::operator())> { };
 
 template <typename Class, typename R, typename... Args>
 struct Extract<R(Class::*)(Args...) const> {
-  typedef isFuture<R> ReturnsFuture;
+  typedef isFutureOrSemiFuture<R> ReturnsFuture;
   typedef Future<typename ReturnsFuture::Inner> Return;
   typedef typename ReturnsFuture::Inner RawReturn;
   typedef typename ArgType<Args...>::FirstArg FirstArg;
@@ -126,7 +144,7 @@ struct Extract<R(Class::*)(Args...) const> {
 
 template <typename Class, typename R, typename... Args>
 struct Extract<R(Class::*)(Args...)> {
-  typedef isFuture<R> ReturnsFuture;
+  typedef isFutureOrSemiFuture<R> ReturnsFuture;
   typedef Future<typename ReturnsFuture::Inner> Return;
   typedef typename ReturnsFuture::Inner RawReturn;
   typedef typename ArgType<Args...>::FirstArg FirstArg;
@@ -134,7 +152,7 @@ struct Extract<R(Class::*)(Args...)> {
 
 template <typename R, typename... Args>
 struct Extract<R (*)(Args...)> {
-  typedef isFuture<R> ReturnsFuture;
+  typedef isFutureOrSemiFuture<R> ReturnsFuture;
   typedef Future<typename ReturnsFuture::Inner> Return;
   typedef typename ReturnsFuture::Inner RawReturn;
   typedef typename ArgType<Args...>::FirstArg FirstArg;
@@ -142,7 +160,7 @@ struct Extract<R (*)(Args...)> {
 
 template <typename R, typename... Args>
 struct Extract<R (&)(Args...)> {
-  typedef isFuture<R> ReturnsFuture;
+  typedef isFutureOrSemiFuture<R> ReturnsFuture;
   typedef Future<typename ReturnsFuture::Inner> Return;
   typedef typename ReturnsFuture::Inner RawReturn;
   typedef typename ArgType<Args...>::FirstArg FirstArg;
index b5c059daef2e0dcfe65a3779d94d982d6c6b9507..c51a6fe9d458f800528479921f4fe99bfc3b2954 100644 (file)
@@ -202,6 +202,44 @@ TEST(SemiFuture, MakeFutureFromSemiFuture) {
   ASSERT_EQ(result, 42);
 }
 
+TEST(SemiFuture, MakeFutureFromSemiFutureReturnFuture) {
+  folly::EventBase e;
+  Promise<int> p;
+  int result{0};
+  auto f = p.getSemiFuture();
+  auto future = std::move(f).via(&e).then([&](int value) {
+    result = value;
+    return folly::makeFuture(std::move(value));
+  });
+  e.loop();
+  EXPECT_EQ(result, 0);
+  EXPECT_FALSE(future.isReady());
+  p.setValue(42);
+  e.loop();
+  EXPECT_TRUE(future.isReady());
+  ASSERT_EQ(future.value(), 42);
+  ASSERT_EQ(result, 42);
+}
+
+TEST(SemiFuture, MakeFutureFromSemiFutureReturnSemiFuture) {
+  folly::EventBase e;
+  Promise<int> p;
+  int result{0};
+  auto f = p.getSemiFuture();
+  auto future = std::move(f).via(&e).then([&](int value) {
+    result = value;
+    return folly::makeSemiFuture(std::move(value));
+  });
+  e.loop();
+  EXPECT_EQ(result, 0);
+  EXPECT_FALSE(future.isReady());
+  p.setValue(42);
+  e.loop();
+  EXPECT_TRUE(future.isReady());
+  ASSERT_EQ(future.value(), 42);
+  ASSERT_EQ(result, 42);
+}
+
 TEST(SemiFuture, MakeFutureFromSemiFutureLValue) {
   folly::EventBase e;
   Promise<int> p;