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 \
///
/// 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);
+++ /dev/null
-/*
- * 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));
-}
-
-}}
+++ /dev/null
-/*
- * 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>
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*);
///
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
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
}
};
-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
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+