/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <folly/futures/Future.h>
#include <folly/futures/ManualExecutor.h>
#include <folly/futures/DrivableExecutor.h>
+#include <folly/dynamic.h>
#include <folly/MPMCQueue.h>
#include <folly/io/async/EventBase.h>
public:
explicit ThreadExecutor(size_t n = 1024)
- : funcs(n), worker(std::bind(&ThreadExecutor::work, this)) {}
+ : funcs(n) {
+ worker = std::thread(std::bind(&ThreadExecutor::work, this));
+ }
~ThreadExecutor() {
done = true;
TEST(Future, coreSize) {
// If this number goes down, it's fine!
// If it goes up, please seek professional advice ;-)
- EXPECT_EQ(128, sizeof(detail::Core<void>));
+ EXPECT_EQ(192, sizeof(detail::Core<void>));
}
// Future
.onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
EXPECT_THROW(f.value(), eggs_t);
}
+
+ // exception_wrapper, return Future<T>
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (exception_wrapper e) { flag(); return makeFuture(); });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
+ // exception_wrapper, return Future<T> but throw
+ {
+ auto f = makeFuture()
+ .then([]{ throw eggs; return 0; })
+ .onError([&] (exception_wrapper e) {
+ flag();
+ throw eggs;
+ return makeFuture<int>(-1);
+ });
+ EXPECT_FLAG();
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ // exception_wrapper, return T
+ {
+ auto f = makeFuture()
+ .then([]{ throw eggs; return 0; })
+ .onError([&] (exception_wrapper e) {
+ flag();
+ return -1;
+ });
+ EXPECT_FLAG();
+ EXPECT_EQ(-1, f.value());
+ }
+
+ // exception_wrapper, return T but throw
+ {
+ auto f = makeFuture()
+ .then([]{ throw eggs; return 0; })
+ .onError([&] (exception_wrapper e) {
+ flag();
+ throw eggs;
+ return -1;
+ });
+ EXPECT_FLAG();
+ EXPECT_THROW(f.value(), eggs_t);
+ }
+
+ // const exception_wrapper&
+ {
+ auto f = makeFuture()
+ .then([] { throw eggs; })
+ .onError([&] (const exception_wrapper& e) {
+ flag();
+ return makeFuture();
+ });
+ EXPECT_FLAG();
+ EXPECT_NO_THROW(f.value());
+ }
+
}
TEST(Future, try) {
}
}
-TEST(Promise, fulfil) {
+TEST(Promise, setWith) {
{
Promise<int> p;
auto f = p.getFuture();
- p.fulfil([] { return 42; });
+ p.setWith([] { return 42; });
EXPECT_EQ(42, f.value());
}
{
Promise<int> p;
auto f = p.getFuture();
- p.fulfil([]() -> int { throw eggs; });
+ p.setWith([]() -> int { throw eggs; });
EXPECT_THROW(f.value(), eggs_t);
}
}
}
}
+TEST(Future, collect) {
+ // success case
+ {
+ vector<Promise<int>> promises(10);
+ vector<Future<int>> futures;
+
+ for (auto& p : promises)
+ futures.push_back(p.getFuture());
+
+ auto allf = collect(futures.begin(), futures.end());
+
+ random_shuffle(promises.begin(), promises.end());
+ for (auto& p : promises) {
+ EXPECT_FALSE(allf.isReady());
+ p.setValue(42);
+ }
+
+ EXPECT_TRUE(allf.isReady());
+ for (auto i : allf.value()) {
+ EXPECT_EQ(42, i);
+ }
+ }
+
+ // failure case
+ {
+ vector<Promise<int>> promises(10);
+ vector<Future<int>> futures;
+
+ for (auto& p : promises)
+ futures.push_back(p.getFuture());
+
+ auto allf = collect(futures.begin(), futures.end());
+
+ random_shuffle(promises.begin(), promises.end());
+ for (int i = 0; i < 10; i++) {
+ if (i < 5) {
+ // everthing goes well so far...
+ EXPECT_FALSE(allf.isReady());
+ promises[i].setValue(42);
+ } else if (i == 5) {
+ // short circuit with an exception
+ EXPECT_FALSE(allf.isReady());
+ promises[i].setException(eggs);
+ EXPECT_TRUE(allf.isReady());
+ } else if (i < 8) {
+ // don't blow up on further values
+ EXPECT_TRUE(allf.isReady());
+ promises[i].setValue(42);
+ } else {
+ // don't blow up on further exceptions
+ EXPECT_TRUE(allf.isReady());
+ promises[i].setException(eggs);
+ }
+ }
+
+ EXPECT_THROW(allf.value(), eggs_t);
+ }
+
+ // void futures success case
+ {
+ vector<Promise<void>> promises(10);
+ vector<Future<void>> futures;
+
+ for (auto& p : promises)
+ futures.push_back(p.getFuture());
+
+ auto allf = collect(futures.begin(), futures.end());
+
+ random_shuffle(promises.begin(), promises.end());
+ for (auto& p : promises) {
+ EXPECT_FALSE(allf.isReady());
+ p.setValue();
+ }
+
+ EXPECT_TRUE(allf.isReady());
+ }
+
+ // void futures failure case
+ {
+ vector<Promise<void>> promises(10);
+ vector<Future<void>> futures;
+
+ for (auto& p : promises)
+ futures.push_back(p.getFuture());
+
+ auto allf = collect(futures.begin(), futures.end());
+
+ random_shuffle(promises.begin(), promises.end());
+ for (int i = 0; i < 10; i++) {
+ if (i < 5) {
+ // everthing goes well so far...
+ EXPECT_FALSE(allf.isReady());
+ promises[i].setValue();
+ } else if (i == 5) {
+ // short circuit with an exception
+ EXPECT_FALSE(allf.isReady());
+ promises[i].setException(eggs);
+ EXPECT_TRUE(allf.isReady());
+ } else if (i < 8) {
+ // don't blow up on further values
+ EXPECT_TRUE(allf.isReady());
+ promises[i].setValue();
+ } else {
+ // don't blow up on further exceptions
+ EXPECT_TRUE(allf.isReady());
+ promises[i].setException(eggs);
+ }
+ }
+
+ EXPECT_THROW(allf.value(), eggs_t);
+ }
+}
TEST(Future, whenAny) {
{
TEST(Future, getFuture_after_setException) {
Promise<void> p;
- p.fulfil([]() -> void { throw std::logic_error("foo"); });
+ p.setWith([]() -> void { throw std::logic_error("foo"); });
EXPECT_THROW(p.getFuture().value(), std::logic_error);
}
EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
- // Fulfil the promise
+ // Fulfill the promise
p.setValue();
}
ptr.reset();
- promise.fulfil([]{return 1l;});
+ promise.setWith([]{return 1l;});
}
TEST(Future, Constructor) {
//auto f2 = []() -> Future<void> { }();
}
+TEST(Future, thenDynamic) {
+ // folly::dynamic has a constructor that takes any T, this test makes
+ // sure that we call the then lambda with folly::dynamic and not
+ // Try<folly::dynamic> because that then fails to compile
+ Promise<folly::dynamic> p;
+ Future<folly::dynamic> f = p.getFuture().then(
+ [](const folly::dynamic& d) {
+ return folly::dynamic(d.asInt() + 3);
+ }
+ );
+ p.setValue(2);
+ EXPECT_EQ(f.get(), 5);
+}
+
TEST(Future, via_then_get_was_racy) {
ThreadExecutor x;
std::unique_ptr<int> val = folly::via(&x)
EXPECT_THROW(f.get(), std::runtime_error);
EXPECT_EQ(2, count);
}
+
+TEST(Future, willEqual) {
+ //both p1 and p2 already fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ p2.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ EXPECT_TRUE(f1.willEqual(f2).get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ p2.setValue(36);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ EXPECT_FALSE(f1.willEqual(f2).get());
+ }
+ //both p1 and p2 not yet fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ p2.setValue(27);
+ EXPECT_TRUE(f3.get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ p2.setValue(36);
+ EXPECT_FALSE(f3.get());
+ }
+ //p1 already fulfilled, p2 not yet fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p2.setValue(27);
+ EXPECT_TRUE(f3.get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ p1.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p2.setValue(36);
+ EXPECT_FALSE(f3.get());
+ }
+ //p2 already fulfilled, p1 not yet fulfilled
+ {
+ Promise<int> p1;
+ Promise<int> p2;
+ p2.setValue(27);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ EXPECT_TRUE(f3.get());
+ }{
+ Promise<int> p1;
+ Promise<int> p2;
+ p2.setValue(36);
+ auto f1 = p1.getFuture();
+ auto f2 = p2.getFuture();
+ auto f3 = f1.willEqual(f2);
+ p1.setValue(27);
+ EXPECT_FALSE(f3.get());
+ }
+}
+
+// Unwrap tests.
+
+// A simple scenario for the unwrap call, when the promise was fulfilled
+// before calling to unwrap.
+TEST(Future, Unwrap_SimpleScenario) {
+ Future<int> encapsulated_future = makeFuture(5484);
+ Future<Future<int>> future = makeFuture(std::move(encapsulated_future));
+ EXPECT_EQ(5484, future.unwrap().value());
+}
+
+// Makes sure that unwrap() works when chaning Future's commands.
+TEST(Future, Unwrap_ChainCommands) {
+ Future<Future<int>> future = makeFuture(makeFuture(5484));
+ auto unwrapped = future.unwrap().then([](int i){ return i; });
+ EXPECT_EQ(5484, unwrapped.value());
+}
+
+// Makes sure that the unwrap call also works when the promise was not yet
+// fulfilled, and that the returned Future<T> becomes ready once the promise
+// is fulfilled.
+TEST(Future, Unwrap_FutureNotReady) {
+ Promise<Future<int>> p;
+ Future<Future<int>> future = p.getFuture();
+ Future<int> unwrapped = future.unwrap();
+ // Sanity - should not be ready before the promise is fulfilled.
+ ASSERT_FALSE(unwrapped.isReady());
+ // Fulfill the promise and make sure the unwrapped future is now ready.
+ p.setValue(makeFuture(5484));
+ ASSERT_TRUE(unwrapped.isReady());
+ EXPECT_EQ(5484, unwrapped.value());
+}
+
+TEST(Reduce, Basic) {
+ auto makeFutures = [](int count) {
+ std::vector<Future<int>> fs;
+ for (int i = 1; i <= count; ++i) {
+ fs.emplace_back(makeFuture(i));
+ }
+ return fs;
+ };
+
+ // Empty (Try)
+ {
+ auto fs = makeFutures(0);
+
+ Future<double> f1 = reduce(fs.begin(), fs.end(), 1.2,
+ [](double a, Try<int>&& b){
+ return a + *b + 0.1;
+ });
+ EXPECT_EQ(1.2, f1.get());
+ }
+
+ // One (Try)
+ {
+ auto fs = makeFutures(1);
+
+ Future<double> f1 = reduce(fs.begin(), fs.end(), 0.0,
+ [](double a, Try<int>&& b){
+ return a + *b + 0.1;
+ });
+ EXPECT_EQ(1.1, f1.get());
+ }
+
+ // Returning values (Try)
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f1 = reduce(fs.begin(), fs.end(), 0.0,
+ [](double a, Try<int>&& b){
+ return a + *b + 0.1;
+ });
+ EXPECT_EQ(6.3, f1.get());
+ }
+
+ // Returning values
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f1 = reduce(fs.begin(), fs.end(), 0.0,
+ [](double a, int&& b){
+ return a + b + 0.1;
+ });
+ EXPECT_EQ(6.3, f1.get());
+ }
+
+ // Returning futures (Try)
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f2 = reduce(fs.begin(), fs.end(), 0.0,
+ [](double a, Try<int>&& b){
+ return makeFuture<double>(a + *b + 0.1);
+ });
+ EXPECT_EQ(6.3, f2.get());
+ }
+
+ // Returning futures
+ {
+ auto fs = makeFutures(3);
+
+ Future<double> f2 = reduce(fs.begin(), fs.end(), 0.0,
+ [](double a, int&& b){
+ return makeFuture<double>(a + b + 0.1);
+ });
+ EXPECT_EQ(6.3, f2.get());
+ }
+}