/*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2014-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include <thread>
-#include <folly/futures/Future.h>
-#include <folly/futures/InlineExecutor.h>
-#include <folly/futures/ManualExecutor.h>
-#include <folly/futures/DrivableExecutor.h>
-#include <folly/Baton.h>
#include <folly/MPMCQueue.h>
-
-#include <thread>
+#include <folly/executors/DrivableExecutor.h>
+#include <folly/executors/InlineExecutor.h>
+#include <folly/executors/ManualExecutor.h>
+#include <folly/futures/Future.h>
+#include <folly/portability/GTest.h>
+#include <folly/synchronization/Baton.h>
using namespace folly;
explicit ManualWaiter(std::shared_ptr<ManualExecutor> ex) : ex(ex) {}
void add(Func f) override {
- ex->add(f);
+ ex->add(std::move(f));
}
void drive() override {
{
t = std::thread([=] {
ManualWaiter eastWaiter(eastExecutor);
- while (!done)
+ while (!done) {
eastWaiter.drive();
+ }
});
}
TEST_F(ViaFixture, threadHops) {
auto westThreadId = std::this_thread::get_id();
- auto f = 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();
- });
+ auto f = via(eastExecutor.get())
+ .then([=](Try<Unit>&& /* 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();
+ });
EXPECT_EQ(f.getVia(waiter.get()), 1);
}
return 1;
}).then([=](int val) {
return makeFuture(val).via(westExecutor.get())
- .then([=](int val) mutable {
+ .then([=](int v) mutable {
EXPECT_EQ(std::this_thread::get_id(), westThreadId);
- return val + 1;
+ return v + 1;
});
}).then([=](int val) {
// even though ultimately the future that triggers this one executed in
}
struct PriorityExecutor : public Executor {
- void add(Func f) override {}
+ void add(Func /* f */) override {}
void addWithPriority(Func f, int8_t priority) override {
int mid = getNumPriorities() / 2;
}
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);
TEST(Via, viaThenGetWasRacy) {
ThreadExecutor x;
- std::unique_ptr<int> val = folly::via(&x)
- .then([] { return folly::make_unique<int>(42); })
- .get();
+ std::unique_ptr<int> val =
+ folly::via(&x).then([] { return std::make_unique<int>(42); }).get();
ASSERT_TRUE(!!val);
EXPECT_EQ(42, *val);
}
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();
class DummyDrivableExecutor : public DrivableExecutor {
public:
- void add(Func f) override {}
+ void add(Func /* f */) override {}
void drive() override { ran = true; }
bool ran{false};
};
}
}
+TEST(Via, getTryVia) {
+ {
+ // non-void
+ ManualExecutor x;
+ auto f = via(&x).then([] { return 23; });
+ EXPECT_FALSE(f.isReady());
+ EXPECT_EQ(23, f.getTryVia(&x).value());
+ }
+
+ {
+ // void
+ ManualExecutor x;
+ auto f = via(&x).then();
+ EXPECT_FALSE(f.isReady());
+ auto t = f.getTryVia(&x);
+ EXPECT_TRUE(t.hasValue());
+ }
+
+ {
+ DummyDrivableExecutor x;
+ auto f = makeFuture(23);
+ EXPECT_EQ(23, f.getTryVia(&x).value());
+ EXPECT_FALSE(x.ran);
+ }
+}
+
TEST(Via, waitVia) {
{
ManualExecutor x;
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([&] {
p.setValue();
});
- while (!done) x.run();
+ while (!done) {
+ x.run();
+ }
t1.join();
t2.join();
}
+TEST(Via, viaDummyExecutorFutureSetValueFirst) {
+ // The callback object will get destroyed when passed to the executor.
+
+ // A promise will be captured by the callback lambda so we can observe that
+ // it will be destroyed.
+ Promise<Unit> captured_promise;
+ auto captured_promise_future = captured_promise.getFuture();
+
+ DummyDrivableExecutor x;
+ auto future = makeFuture().via(&x).then(
+ [c = std::move(captured_promise)] { return 42; });
+
+ EXPECT_THROW(future.get(std::chrono::seconds(5)), BrokenPromise);
+ EXPECT_THROW(
+ captured_promise_future.get(std::chrono::seconds(5)), BrokenPromise);
+}
+
+TEST(Via, viaDummyExecutorFutureSetCallbackFirst) {
+ // The callback object will get destroyed when passed to the executor.
+
+ // A promise will be captured by the callback lambda so we can observe that
+ // it will be destroyed.
+ Promise<Unit> captured_promise;
+ auto captured_promise_future = captured_promise.getFuture();
+
+ DummyDrivableExecutor x;
+ Promise<Unit> trigger;
+ auto future = trigger.getFuture().via(&x).then(
+ [c = std::move(captured_promise)] { return 42; });
+ trigger.setValue();
+
+ EXPECT_THROW(future.get(std::chrono::seconds(5)), BrokenPromise);
+ EXPECT_THROW(
+ captured_promise_future.get(std::chrono::seconds(5)), BrokenPromise);
+}
+
+TEST(Via, viaExecutorDiscardsTaskFutureSetValueFirst) {
+ // The callback object will get destroyed when the ManualExecutor runs out
+ // of scope.
+
+ // A promise will be captured by the callback lambda so we can observe that
+ // it will be destroyed.
+ Promise<Unit> captured_promise;
+ auto captured_promise_future = captured_promise.getFuture();
+
+ Optional<Future<int>> future;
+ {
+ ManualExecutor x;
+ future = makeFuture().via(&x).then(
+ [c = std::move(captured_promise)] { return 42; });
+ }
+
+ EXPECT_THROW(future->get(std::chrono::seconds(5)), BrokenPromise);
+ EXPECT_THROW(
+ captured_promise_future.get(std::chrono::seconds(5)), BrokenPromise);
+}
+
+TEST(Via, viaExecutorDiscardsTaskFutureSetCallbackFirst) {
+ // The callback object will get destroyed when the ManualExecutor runs out
+ // of scope.
+
+ // A promise will be captured by the callback lambda so we can observe that
+ // it will be destroyed.
+ Promise<Unit> captured_promise;
+ auto captured_promise_future = captured_promise.getFuture();
+
+ Optional<Future<int>> future;
+ {
+ ManualExecutor x;
+ Promise<Unit> trigger;
+ future = trigger.getFuture().via(&x).then(
+ [c = std::move(captured_promise)] { return 42; });
+ trigger.setValue();
+ }
+
+ EXPECT_THROW(future->get(std::chrono::seconds(5)), BrokenPromise);
+ EXPECT_THROW(
+ captured_promise_future.get(std::chrono::seconds(5)), BrokenPromise);
+}
+
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();
x.run();
EXPECT_EQ(2, count);
}
+
+TEST(ViaFunc, moveOnly) {
+ ManualExecutor x;
+ auto intp = std::make_unique<int>(42);
+
+ EXPECT_EQ(42, via(&x, [intp = std::move(intp)] { return *intp; }).getVia(&x));
+}