Remove Later
authorHans Fugal <fugalh@fb.com>
Fri, 5 Dec 2014 17:33:25 +0000 (09:33 -0800)
committerDave Watson <davejwatson@fb.com>
Thu, 11 Dec 2014 16:00:48 +0000 (08:00 -0800)
Summary: dalek-exterminate

Test Plan:
Moved `Later` tests to `via` tests.
fbgs
contbuild

Reviewed By: jsedgwick@fb.com

Subscribers: fbcode-common-diffs@, adityab, lins, trunkagent, fugalh, exa, njormrod, folly-diffs@, hannesr

FB internal diff: D1714862

Tasks: 5409538

Signature: t1:1714862:1417621949:f63f49e1093a021170d2346e8e673db042d2bc56

folly/Makefile.am
folly/wangle/Future.h
folly/wangle/Later-inl.h [deleted file]
folly/wangle/Later.h [deleted file]
folly/wangle/OpaqueCallbackShunt.h
folly/wangle/README.md
folly/wangle/detail/Core.h
folly/wangle/test/LaterTest.cpp [deleted file]
folly/wangle/test/ViaTest.cpp [new file with mode: 0644]

index 59e450238c9bd963ae0c9959e798110d302b2528..bd5cc48dd1532f3e7255653cff63904215cc7c38 100644 (file)
@@ -237,8 +237,6 @@ nobase_follyinclude_HEADERS = \
        wangle/Future-inl.h \
        wangle/Future.h \
        wangle/InlineExecutor.h \
-       wangle/Later-inl.h \
-       wangle/Later.h \
        wangle/ManualExecutor.h \
        wangle/OpaqueCallbackShunt.h \
        wangle/Promise-inl.h \
index 9c19d37f39ba6bf13f63ecfa2a59b13762d05e3b..57f8636b8babaea859402bc9b3924d35a50c3650 100644 (file)
@@ -91,8 +91,6 @@ class Future {
   ///
   ///   f = f.via(e).then(a);
   ///   f.then(b);
-  ///
-  /// If you need something like that, use a Later.
   template <typename Executor>
   Future<T> via(Executor* executor);
 
diff --git a/folly/wangle/Later-inl.h b/folly/wangle/Later-inl.h
deleted file mode 100644 (file)
index b054d6d..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/wangle/Executor.h>
-#include <folly/wangle/Future.h>
-#include <folly/Optional.h>
-
-namespace folly { namespace wangle {
-
-template <typename T>
-struct isLater {
-  static const bool value = false;
-};
-
-template <typename T>
-struct isLater<Later<T> > {
-  static const bool value = true;
-};
-
-template <typename T>
-struct isLaterOrFuture {
-  static const bool value = false;
-};
-
-template <typename T>
-struct isLaterOrFuture<Later<T>> {
-  static const bool value = true;
-};
-
-template <typename T>
-struct isLaterOrFuture<Future<T>> {
-  static const bool value = true;
-};
-
-template <typename T>
-template <class U, class Unused, class Unused2>
-Later<T>::Later() {
-  future_ = starter_.getFuture();
-}
-
-template <class T>
-Later<T>::Later(Future<T>&& f) {
-  MoveWrapper<Future<T>> fw(std::move(f));
-  *this = Later<void>()
-    .then([fw](Try<void>&&) mutable {
-      return std::move(*fw);
-    });
-}
-
-template <typename T>
-Later<T>::Later(Promise<void>&& starter)
-  : starter_(std::forward<Promise<void>>(starter)) { }
-
-template <class T>
-template <class U, class Unused, class Unused2>
-Later<T>::Later(U&& input) {
-  folly::MoveWrapper<Promise<U>> promise;
-  folly::MoveWrapper<U> inputm(std::forward<U>(input));
-  future_ = promise->getFuture();
-  starter_.getFuture().then([=](Try<void>&& t) mutable {
-    promise->setValue(std::move(*inputm));
-  });
-}
-
-template <typename T>
-Later<T>::Later(std::exception_ptr const& eptr) {
-  folly::MoveWrapper<Promise<T>> promise;
-  future_ = promise->getFuture();
-  starter_.getFuture().then([=](Try<void>&& t) mutable {
-    promise->setException(eptr);
-  });
-}
-
-template <typename T>
-template <typename E, class Unused>
-Later<T>::Later(E const& e) :
-    Later<T>::Later(std::make_exception_ptr<E>(e)) {
-}
-
-template <class T>
-template <class U, class Unused, class Unused2>
-Later<T>::Later(std::function<void(std::function<void(U&&)>&&)>&& fn) {
-  folly::MoveWrapper<Promise<U>> promise;
-  future_ = promise->getFuture();
-  starter_.getFuture().then([=](Try<void>&& t) mutable {
-    fn([=](U&& output) mutable {
-      promise->setValue(std::move(output));
-    });
-  });
-}
-
-template <class T>
-template <class F>
-typename std::enable_if<
-  !isLaterOrFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-  Later<typename std::result_of<F(Try<T>&&)>::type> >::type
-Later<T>::then(F&& fn) {
-  typedef typename std::result_of<F(Try<T>&&)>::type B;
-
-  Later<B> later(std::move(starter_));
-  later.future_ = future_->then(std::forward<F>(fn));
-  return later;
-}
-
-template <class T>
-template <class F>
-typename std::enable_if<
-  isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-  Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-Later<T>::then(F&& fn) {
-  typedef typename std::result_of<F(Try<T>&&)>::type::value_type B;
-
-  Later<B> later(std::move(starter_));
-  later.future_ = future_->then(std::move(fn));
-  return later;
-}
-
-template <class T>
-template <class F>
-typename std::enable_if<
-  isLater<typename std::result_of<F(Try<T>&&)>::type>::value,
-  Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-Later<T>::then(F&& fn) {
-  typedef typename std::result_of<F(Try<T>&&)>::type::value_type B;
-
-  folly::MoveWrapper<Promise<B>> promise;
-  folly::MoveWrapper<F> fnm(std::move(fn));
-  Later<B> later(std::move(starter_));
-  later.future_ = promise->getFuture();
-  future_->then([=](Try<T>&& t) mutable {
-    (*fnm)(std::move(t))
-    .then([=](Try<B>&& t2) mutable {
-      promise->fulfilTry(std::move(t2));
-    })
-    .launch();
-  });
-  return later;
-}
-
-template <class T>
-Later<T> Later<T>::via(Executor* executor) {
-  folly::MoveWrapper<Promise<T>> promise;
-  Later<T> later(std::move(starter_));
-  later.future_ = promise->getFuture();
-
-  future_->setCallback_([executor, promise](Try<T>&& t) mutable {
-    folly::MoveWrapper<Try<T>> tt(std::move(t));
-    executor->add([promise, tt]() mutable {
-      promise->fulfilTry(std::move(*tt));
-    });
-  });
-
-  return later;
-}
-
-template <class T>
-Future<T> Later<T>::launch() {
-  starter_.setValue();
-  return std::move(*future_);
-}
-
-template <class T>
-Later<std::vector<Try<T>>> whenAllLater(std::vector<Later<T>>&& laters) {
-  if (laters.size() == 0) {
-    return Later<std::vector<Try<T>>>(std::vector<Try<T>>());
-  }
-
-  auto ctx = new detail::WhenAllLaterContext<T>();
-  ctx->total = laters.size();
-  ctx->results.resize(ctx->total);
-
-  MoveWrapper<std::vector<Later<T>>> mlaters{std::move(laters)};
-
-  std::function<void(std::function<void(std::vector<Try<T>>&&)>&&)> wrapper =
-    [ctx, mlaters](std::function<void(std::vector<Try<T>>&&)>&& fn) mutable {
-      ctx->fn = std::move(fn);
-      size_t i = 0;
-      for (auto& l : *mlaters) {
-        l.then([ctx, i](Try<T>&& t) {
-            ctx->results[i] = std::move(t);
-            if (++ctx->count == ctx->total) {
-              ctx->fn(std::move(ctx->results));
-              delete ctx;
-            }
-          }).launch();
-          ++i;
-      }
-    };
-  return Later<std::vector<Try<T>>>(std::move(wrapper));
-}
-
-}}
diff --git a/folly/wangle/Later.h b/folly/wangle/Later.h
deleted file mode 100644 (file)
index 329748e..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/wangle/Deprecated.h>
-#include <folly/wangle/Executor.h>
-#include <folly/wangle/Future.h>
-#include <folly/Optional.h>
-
-namespace folly { namespace wangle {
-
-template <typename T> struct isLaterOrFuture;
-template <typename T> struct isLater;
-
-/*
- * Later is like a cold Future, but makes it easier to avoid triggering until
- * later, because it must be triggered explicitly. An equivalence example will
- * help differentiate:
- *
- *   Later<Foo> later =
- *     Later<Foo>(std::move(foo))
- *     .then(cb1)
- *     .via(ex1)
- *     .then(cb2)
- *     .then(cb3)
- *     .via(ex2)
- *     .then(cb4)
- *     .then(cb5);
- *   ...
- *   later.launch();
- *
- *   Future<Foo> coldFuture = makeFuture(std::move(foo));
- *   coldFuture.deactivate();
- *   coldFuture
- *     .then(cb1)
- *     .via(ex1)
- *     .then(cb2)
- *     .then(cb3)
- *     .via(ex2)
- *     .then(cb4)
- *     .then(cb5);
- *   ...
- *   coldFuture.activate();
- *
- * Using a Later means you don't have to grab a handle to the first Future and
- * deactivate it.
- *
- * Later used to be a workaround to the thread-unsafe nature of Future
- * chaining, but that has changed and there is no need to use Later if your
- * only goal is to traverse thread boundaries with executors. In that case,
- * just use Future::via().
- *
- * Here is an example of a workflow:
- *
- * Later<ClientRequest> later(std::move(request));
- *
- * auto future = later.
- *   .via(cpuExecutor)
- *   .then([=](Try<ClientRequest>&& t) { return doCpuWork(t.value()); })
- *   .via(diskExecutor)
- *   .then([=](Try<CpuResponse>&& t) { return doDiskWork(t.value()); })
- *   .via(serverExecutor)
- *   .then([=]Try<DiskResponse>&& t) { return sendClientResponse(t.value()); })
- *   .launch();
- */
-// DEPRECATED. Just use Future::via() to accomplish the same thing. If it's
-// not obvious how, feel free to reach out.
-template <class T>
-class DEPRECATED Later {
- public:
-  typedef T value_type;
-
-  /*
-   * This default constructor is used to build an asynchronous workflow that
-   * takes no input.
-   */
-  template <class U = void,
-            class = typename std::enable_if<std::is_void<U>::value>::type,
-            class = typename std::enable_if<std::is_same<T, U>::value>::type>
-  Later();
-
-  /*
-   * Lift a Future into a Later
-   */
-  /* implicit */ Later(Future<T>&& f);
-
-  /*
-   * This constructor is used to build an asynchronous workflow that takes a
-   * value as input, and that value is passed in.
-   */
-  template <class U,
-            class = typename std::enable_if<!std::is_void<U>::value>::type,
-            class = typename std::enable_if<std::is_same<T, U>::value>::type>
-  explicit Later(U&& input);
-
-  /*
-   * This constructor is used to build an asynchronous workflow that takes an
-   * exception_ptr as input, and throws it on completion.
-   */
-  explicit Later(std::exception_ptr const&);
-
-  /*
-   * This constructor is used to build an asynchronous workflow that takes an
-   * exception as input, and throws it on completion.
-   */
-  template <class E,
-            class = typename std::enable_if<
-                std::is_base_of<std::exception, E>::value>::type>
-  explicit Later(E const& e);
-
-  /*
-   * This constructor is used to wrap a pre-existing cob-style asynchronous api
-   * so that it can be used in wangle. wangle provides the callback to this
-   * pre-existing api, and this callback will fulfill a promise so as to
-   * incorporate this api into the workflow.
-   *
-   * Example usage:
-   *
-   * // This adds two ints asynchronously. cob is called in another thread.
-   * void addAsync(int a, int b, std::function<void(int&&)>&& cob);
-   *
-   * Later<int> asyncWrapper([=](std::function<void(int&&)>&& fn) {
-   *   addAsync(1, 2, std::move(fn));
-   * });
-   */
-  // TODO we should implement a makeFuture-ish with this pattern too, now.
-  template <class U,
-            class = typename std::enable_if<!std::is_void<U>::value>::type,
-            class = typename std::enable_if<std::is_same<T, U>::value>::type>
-  explicit Later(std::function<void(std::function<void(U&&)>&&)>&& fn);
-
-  /*
-   * then() adds additional work to the end of the workflow. If the lambda
-   * provided to then() returns a future, that future must be fulfilled in the
-   * same thread of the last set executor (either at constructor or from a call
-   * to via()).
-   */
-  template <class F>
-  typename std::enable_if<
-    !isLaterOrFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-    Later<typename std::result_of<F(Try<T>&&)>::type> >::type
-  then(F&& fn);
-
-  template <class F>
-  typename std::enable_if<
-    isFuture<typename std::result_of<F(Try<T>&&)>::type>::value,
-    Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-  then(F&& fn);
-
-  /*
-   * If the function passed to then() returns a Later<T>, calls to then() will
-   * be chained to the new Later before launching the new Later.
-   *
-   * This can be used to build asynchronous modules that can be called from a
-   * user thread and completed in a callback thread.
-   *
-   * Using the Later(std::function<void(std::function<void(T&&)>)>&& fn)
-   * constructor, you can wrap existing asynchronous modules with a Later and
-   * can chain it to wangle asynchronous workflows via this call.
-   */
-  template <class F>
-  typename std::enable_if<
-    isLater<typename std::result_of<F(Try<T>&&)>::type>::value,
-    Later<typename std::result_of<F(Try<T>&&)>::type::value_type> >::type
-  then(F&& fn);
-
-
-  /// Variant where func is an ordinary function (static method, method)
-  /// Must return a Later
-  template <class R>
-  typename std::enable_if<isLater<R>::value, R>::type
-  inline then(R(*func)(Try<T>&&)) {
-    return then([func](Try<T>&& t) {
-      return (*func)(std::move(t));
-    });
-  }
-
-  /// Variant where func is an member function
-  /// Must return a Later
-  template <class R, class Caller>
-  typename std::enable_if<isLater<R>::value, R>::type
-  inline then(Caller *instance, R(Caller::*func)(Try<T>&&)) {
-    return then([instance, func](Try<T>&& t) {
-      return (instance->*func)(std::move(t));
-    });
-  }
-
-  /*
-   * Resets the executor - all then() calls made after the call to via() will be
-   * made in the new executor. The Executor must outlive.
-   */
-  Later<T> via(Executor* executor);
-
-  /*
-   * Starts the workflow. The function provided in the constructor will be
-   * called in the executor provided in the constructor. Subsequent then()
-   * calls will be made, potentially changing threads if a via() call is made.
-   * The future returned will be fulfilled in the last executor.
-   */
-  Future<T> launch();
-
- private:
-  Promise<void> starter_;
-  folly::Optional<Future<T>> future_;
-
-  struct hide { };
-
-  explicit Later(Promise<void>&& starter);
-
-  template <class U>
-  friend class Later;
-};
-
-// See Future.whenAll
-template <class T>
-Later<std::vector<Try<T>>> whenAllLater(std::vector<Later<T>>&& laters);
-
-}}
-
-#include <folly/wangle/Later-inl.h>
index a5aebf8e41bd23b6c6d632d4b44bc2594c24d492..6cd325ec8889421e8991ccee0d1db3433fe3b4da 100644 (file)
@@ -21,7 +21,7 @@
 namespace folly { namespace wangle {
 
 /// These classes help you wrap an existing C style callback function
-/// into a Future/Later.
+/// into a Future.
 ///
 ///   void legacy_send_async(..., void (*cb)(void*), void*);
 ///
@@ -54,37 +54,4 @@ private:
   T obj_;
 };
 
-/// Variant that returns a Later instead of a Future
-///
-///   Later<int> wrappedSendAsyncLater(int i) {
-///     folly::MoveWrapper<int> wrapped(std::move(i));
-///     return Later<int>(
-///       [..., wrapped](std::function<void(int&&)>&& fn) mutable {
-///         auto handle = new OpaqueCallbackLaterShunt<int>(
-///           std::move(*wrapped), std::move(fn));
-///         legacy_send_async(...,
-///          OpaqueCallbackLaterShunt<int>::callback, handle);
-///       });
-///   }
-///
-/// Depending on your compiler's kung-fu knowledge, you might need to assign
-/// the lambda to a std::function<void(std::function<void(int&&)>&&)> temporary
-/// variable before std::moving into it into the later.
-
-template <typename T>
-class OpaqueCallbackLaterShunt {
-public:
-  explicit
-  OpaqueCallbackLaterShunt(T&& obj, std::function<void(T&&)>&& fn)
-   : fn_(std::move(fn)), obj_(std::move(obj)) { }
-  static void callback(void* arg) {
-    std::unique_ptr<OpaqueCallbackLaterShunt<T>> handle(
-      static_cast<OpaqueCallbackLaterShunt<T>*>(arg));
-    handle->fn_(std::move(handle->obj_));
-  }
-private:
-  std::function<void(T&&)> fn_;
-  T obj_;
-};
-
 }} // folly::wangle
index 57f1230acaf496bf5512d7555fc21ce12a4226f8..cb88fe36991891623f0dda22d387577de2eb8eb2 100644 (file)
@@ -207,13 +207,6 @@ You can still have a race after `via` if you break it into multiple statements,
 f = f.via(e1).then(y1).then(y2); // nothing racy here
 f2.then(y3); // racy
 ```
-If you want more control over the delayed execution, check out `Later`.
-```C++
-Later<void> later;
-later = later.via(e1).then(y1).then(y2); // nothing racy here
-later = later.then(y3); // nor here
-later.launch(); // explicit launch
-```
 
 ## You make me Promises, Promises
 
index 1113e0b2411373cd4efb55065b47efdde2a07813..03d09b747547041585a6868274c6f2aa666f8ce5 100644 (file)
@@ -289,13 +289,4 @@ struct WhenAnyContext {
   }
 };
 
-template <typename T>
-struct WhenAllLaterContext {
-  explicit WhenAllLaterContext() : count(0), total(0) {}
-  std::function<void(std::vector<Try<T>>&&)> fn;
-  std::vector<Try<T> > results;
-  std::atomic<size_t> count;
-  size_t total;
-};
-
 }}} // namespace
diff --git a/folly/wangle/test/LaterTest.cpp b/folly/wangle/test/LaterTest.cpp
deleted file mode 100644 (file)
index 97dfd9f..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <thread>
-
-#include <folly/wangle/ManualExecutor.h>
-#include <folly/wangle/InlineExecutor.h>
-#include <folly/wangle/Later.h>
-
-using namespace folly::wangle;
-
-struct ManualWaiter {
-  explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
-
-  void makeProgress() {
-    ex->wait();
-    ex->run();
-  }
-
-  std::shared_ptr<ManualExecutor> ex;
-};
-
-struct LaterFixture : public testing::Test {
-  LaterFixture() :
-    westExecutor(new ManualExecutor),
-    eastExecutor(new ManualExecutor),
-    waiter(new ManualWaiter(westExecutor)),
-    done(false)
-  {
-    t = std::thread([=] {
-      ManualWaiter eastWaiter(eastExecutor);
-      while (!done)
-        eastWaiter.makeProgress();
-      });
-  }
-
-  ~LaterFixture() {
-    done = true;
-    eastExecutor->add([=]() { });
-    t.join();
-  }
-
-  void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
-    eastExecutor->add([=]() {
-      cob(a + b);
-    });
-  }
-
-  Later<void> later;
-  std::shared_ptr<ManualExecutor> westExecutor;
-  std::shared_ptr<ManualExecutor> eastExecutor;
-  std::shared_ptr<ManualWaiter> waiter;
-  InlineExecutor inlineExecutor;
-  bool done;
-  std::thread t;
-};
-
-TEST(Later, construct_and_launch) {
-  bool fulfilled = false;
-  auto later = Later<void>().then([&](Try<void>&& t) {
-    fulfilled = true;
-    return makeFuture<int>(1);
-  });
-
-  // has not started yet.
-  EXPECT_FALSE(fulfilled);
-
-  EXPECT_EQ(later.launch().value(), 1);
-  EXPECT_TRUE(fulfilled);
-}
-
-TEST(Later, exception_on_launch) {
-  auto later = Later<void>(std::runtime_error("E"));
-  EXPECT_THROW(later.launch().value(), std::runtime_error);
-}
-
-TEST(Later, then_value) {
-  auto future = Later<int>(std::move(1))
-    .then([](Try<int>&& t) {
-      return t.value() == 1;
-    })
-    .launch();
-
-  EXPECT_TRUE(future.value());
-}
-
-TEST(Later, then_future) {
-  auto future = Later<int>(1)
-    .then([](Try<int>&& t) {
-      return makeFuture(t.value() == 1);
-    })
-    .launch();
-  EXPECT_TRUE(future.value());
-}
-
-static Later<std::string> doWorkStatic(Try<std::string>&& t) {
-  return Later<std::string>(t.value() + ";static");
-}
-
-TEST(Later, then_function) {
-  struct Worker {
-    Later<std::string> doWork(Try<std::string>&& t) {
-      return Later<std::string>(t.value() + ";class");
-    }
-    static Later<std::string> doWorkStatic(Try<std::string>&& t) {
-      return Later<std::string>(t.value() + ";class-static");
-    }
-  } w;
-
-  auto f = Later<std::string>(std::string("start"))
-    .then(doWorkStatic)
-    .then(Worker::doWorkStatic)
-    .then(&w, &Worker::doWork)
-    .launch();
-
-  EXPECT_EQ(f.value(), "start;static;class-static;class");
-}
-
-TEST_F(LaterFixture, thread_hops) {
-  auto westThreadId = std::this_thread::get_id();
-  auto future = later.via(eastExecutor.get()).then([=](Try<void>&& t) {
-    EXPECT_NE(std::this_thread::get_id(), westThreadId);
-    return makeFuture<int>(1);
-  }).via(westExecutor.get()
-  ).then([=](Try<int>&& t) {
-    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-    return t.value();
-  }).launch();
-  while (!future.isReady()) {
-    waiter->makeProgress();
-  }
-  EXPECT_EQ(future.value(), 1);
-}
-
-TEST_F(LaterFixture, wrapping_preexisting_async_modules) {
-  auto westThreadId = std::this_thread::get_id();
-  std::function<void(std::function<void(int&&)>&&)> wrapper =
-    [=](std::function<void(int&&)>&& fn) {
-      addAsync(2, 2, std::move(fn));
-    };
-  auto future = Later<int>(std::move(wrapper))
-  .via(westExecutor.get())
-  .then([=](Try<int>&& t) {
-    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-    return t.value();
-  })
-  .launch();
-  while (!future.isReady()) {
-    waiter->makeProgress();
-  }
-  EXPECT_EQ(future.value(), 4);
-}
-
-TEST_F(LaterFixture, chain_laters) {
-  auto westThreadId = std::this_thread::get_id();
-  auto future = later.via(eastExecutor.get()).then([=](Try<void>&& t) {
-    EXPECT_NE(std::this_thread::get_id(), westThreadId);
-    return makeFuture<int>(1);
-  }).then([=](Try<int>&& t) {
-    int val = t.value();
-    return Later<int>(std::move(val)).via(westExecutor.get())
-      .then([=](Try<int>&& t) mutable {
-        EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-        return t.value();
-      });
-  }).then([=](Try<int>&& t) {
-    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
-    return t.value();
-  }).launch();
-
-  while (!future.isReady()) {
-    waiter->makeProgress();
-  }
-  EXPECT_EQ(future.value(), 1);
-}
-
-TEST(Later, when_all_later) {
-  size_t done = 0;
-  std::vector<Later<int>> laters;
-  laters.emplace_back(Later<int>(1).then([&](Try<int>&& i) mutable {
-    done += i.value(); return 8;
-  }));
-  laters.emplace_back(Later<int>(2).then([&](Try<int>&& i) mutable {
-    done += i.value(); return 16;
-  }));
-  laters.emplace_back(Later<int>(4).then([&](Try<int>&& i) mutable {
-    done += i.value(); return 32;
-  }));
-  whenAllLater(std::move(laters))
-  .then([&](Try<std::vector<Try<int>>>&& v) mutable {
-    for (const auto& i : v.value()) {
-      done += i.value();
-    }
-  }).launch();
-  EXPECT_EQ(done, 63);
-}
diff --git a/folly/wangle/test/ViaTest.cpp b/folly/wangle/test/ViaTest.cpp
new file mode 100644 (file)
index 0000000..9a3758c
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <thread>
+
+#include <folly/wangle/Future.h>
+#include <folly/wangle/InlineExecutor.h>
+#include <folly/wangle/ManualExecutor.h>
+
+using namespace folly::wangle;
+
+struct ManualWaiter {
+  explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
+
+  void makeProgress() {
+    ex->wait();
+    ex->run();
+  }
+
+  std::shared_ptr<ManualExecutor> ex;
+};
+
+struct ViaFixture : public testing::Test {
+  ViaFixture() :
+    future_(makeFuture().deactivate()),
+    westExecutor(new ManualExecutor),
+    eastExecutor(new ManualExecutor),
+    waiter(new ManualWaiter(westExecutor)),
+    done(false)
+  {
+    t = std::thread([=] {
+      ManualWaiter eastWaiter(eastExecutor);
+      while (!done)
+        eastWaiter.makeProgress();
+      });
+  }
+
+  ~ViaFixture() {
+    done = true;
+    eastExecutor->add([=]() { });
+    t.join();
+  }
+
+  void addAsync(int a, int b, std::function<void(int&&)>&& cob) {
+    eastExecutor->add([=]() {
+      cob(a + b);
+    });
+  }
+
+  Future<void> future_;
+  std::shared_ptr<ManualExecutor> westExecutor;
+  std::shared_ptr<ManualExecutor> eastExecutor;
+  std::shared_ptr<ManualWaiter> waiter;
+  InlineExecutor inlineExecutor;
+  bool done;
+  std::thread t;
+};
+
+TEST(Via, exception_on_launch) {
+  auto future = makeFuture<int>(std::runtime_error("E"));
+  EXPECT_THROW(future.value(), std::runtime_error);
+}
+
+TEST(Via, then_value) {
+  auto future = makeFuture(std::move(1))
+    .then([](Try<int>&& t) {
+      return t.value() == 1;
+    })
+    ;
+
+  EXPECT_TRUE(future.value());
+}
+
+TEST(Via, then_future) {
+  auto future = makeFuture(1)
+    .then([](Try<int>&& t) {
+      return makeFuture(t.value() == 1);
+    })
+    ;
+  EXPECT_TRUE(future.value());
+}
+
+static Future<std::string> doWorkStatic(Try<std::string>&& t) {
+  return makeFuture(t.value() + ";static");
+}
+
+TEST(Via, then_function) {
+  struct Worker {
+    Future<std::string> doWork(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class");
+    }
+    static Future<std::string> doWorkStatic(Try<std::string>&& t) {
+      return makeFuture(t.value() + ";class-static");
+    }
+  } w;
+
+  auto f = makeFuture(std::string("start"))
+    .then(doWorkStatic)
+    .then(Worker::doWorkStatic)
+    .then(&w, &Worker::doWork)
+    ;
+
+  EXPECT_EQ(f.value(), "start;static;class-static;class");
+}
+
+TEST_F(ViaFixture, thread_hops) {
+  auto westThreadId = std::this_thread::get_id();
+  auto f = future_.via(eastExecutor.get()).then([=](Try<void>&& t) {
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return makeFuture<int>(1);
+  }).via(westExecutor.get()
+  ).then([=](Try<int>&& t) {
+    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+    return t.value();
+  });
+  while (!f.isReady()) {
+    waiter->makeProgress();
+  }
+  EXPECT_EQ(f.value(), 1);
+}
+
+TEST_F(ViaFixture, chain_vias) {
+  auto westThreadId = std::this_thread::get_id();
+  auto f = future_.via(eastExecutor.get()).then([=](Try<void>&& t) {
+    EXPECT_NE(std::this_thread::get_id(), westThreadId);
+    return makeFuture<int>(1);
+  }).then([=](Try<int>&& t) {
+    int val = t.value();
+    return makeFuture(std::move(val)).via(westExecutor.get())
+      .then([=](Try<int>&& t) mutable {
+        EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+        return t.value();
+      });
+  }).then([=](Try<int>&& t) {
+    EXPECT_EQ(std::this_thread::get_id(), westThreadId);
+    return t.value();
+  });
+
+  while (!f.isReady()) {
+    waiter->makeProgress();
+  }
+  EXPECT_EQ(f.value(), 1);
+}
+