X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ffutures%2Ftest%2FFutureTest.cpp;h=5d171bb9732f50f9c0e12476072b3a79d2e9a253;hb=5180b66230ee290a22bdba66f81e9f33e0216dd9;hp=00a56be6d56aa93529f29fd62bb984db580d7186;hpb=75aef07cbbc9112459c8b2a5b6ee4fdf6b2229ab;p=folly.git diff --git a/folly/futures/test/FutureTest.cpp b/folly/futures/test/FutureTest.cpp index 00a56be6..5d171bb9 100644 --- a/folly/futures/test/FutureTest.cpp +++ b/folly/futures/test/FutureTest.cpp @@ -14,87 +14,30 @@ * limitations under the License. */ +#include + +#include +#include +#include +#include +#include + #include #include -#include -#include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include using namespace folly; -using std::pair; -using std::string; -using std::unique_ptr; -using std::vector; -using std::chrono::milliseconds; #define EXPECT_TYPE(x, T) \ EXPECT_TRUE((std::is_same::value)) -/// Simple executor that does work in another thread -class ThreadExecutor : public Executor { - folly::MPMCQueue funcs; - std::atomic done {false}; - std::thread worker; - folly::Baton<> baton; - - void work() { - baton.post(); - Func fn; - while (!done) { - while (!funcs.isEmpty()) { - funcs.blockingRead(fn); - fn(); - } - } - } - - public: - explicit ThreadExecutor(size_t n = 1024) - : funcs(n) { - worker = std::thread(std::bind(&ThreadExecutor::work, this)); - } - - ~ThreadExecutor() { - done = true; - funcs.write([]{}); - worker.join(); - } - - void add(Func fn) override { - funcs.blockingWrite(std::move(fn)); - } - - void waitForStartup() { - baton.wait(); - } -}; - typedef FutureException eggs_t; static eggs_t eggs("eggs"); -// Core - -TEST(Future, coreSize) { - // If this number goes down, it's fine! - // If it goes up, please seek professional advice ;-) - EXPECT_EQ(192, sizeof(detail::Core)); -} - // Future TEST(Future, onError) { @@ -323,26 +266,6 @@ TEST(Future, onError) { } -TEST(Future, try) { - class A { - public: - A(int x) : x_(x) {} - - int x() const { - return x_; - } - private: - int x_; - }; - - A a(5); - Try t_a(std::move(a)); - - Try t_void; - - EXPECT_EQ(5, t_a.value().x()); -} - TEST(Future, special) { EXPECT_FALSE(std::is_copy_constructible>::value); EXPECT_FALSE(std::is_copy_assignable>::value); @@ -351,20 +274,33 @@ TEST(Future, special) { } TEST(Future, then) { - auto f = makeFuture("0") - .then([](){ return makeFuture("1"); }) - .then([](Try&& t) { return makeFuture(t.value() + ";2"); }) - .then([](const Try&& t) { return makeFuture(t.value() + ";3"); }) - .then([](Try& t) { return makeFuture(t.value() + ";4"); }) - .then([](const Try& t) { return makeFuture(t.value() + ";5"); }) - .then([](Try t) { return makeFuture(t.value() + ";6"); }) - .then([](const Try t) { return makeFuture(t.value() + ";7"); }) - .then([](string&& s) { return makeFuture(s + ";8"); }) - .then([](const string&& s) { return makeFuture(s + ";9"); }) - .then([](string& s) { return makeFuture(s + ";10"); }) - .then([](const string& s) { return makeFuture(s + ";11"); }) - .then([](string s) { return makeFuture(s + ";12"); }) - .then([](const string s) { return makeFuture(s + ";13"); }) + auto f = makeFuture("0") + .then([](){ + return makeFuture("1"); }) + .then([](Try&& t) { + return makeFuture(t.value() + ";2"); }) + .then([](const Try&& t) { + return makeFuture(t.value() + ";3"); }) + .then([](Try& t) { + return makeFuture(t.value() + ";4"); }) + .then([](const Try& t) { + return makeFuture(t.value() + ";5"); }) + .then([](Try t) { + return makeFuture(t.value() + ";6"); }) + .then([](const Try t) { + return makeFuture(t.value() + ";7"); }) + .then([](std::string&& s) { + return makeFuture(s + ";8"); }) + .then([](const std::string&& s) { + return makeFuture(s + ";9"); }) + .then([](std::string& s) { + return makeFuture(s + ";10"); }) + .then([](const std::string& s) { + return makeFuture(s + ";11"); }) + .then([](std::string s) { + return makeFuture(s + ";12"); }) + .then([](const std::string s) { + return makeFuture(s + ";13"); }) ; EXPECT_EQ(f.value(), "1;2;3;4;5;6;7;8;9;10;11;12;13"); } @@ -433,21 +369,21 @@ TEST(Future, thenValueFuture) { EXPECT_TRUE(flag); flag = false; } -static string doWorkStatic(Try&& t) { +static std::string doWorkStatic(Try&& t) { return t.value() + ";static"; } TEST(Future, thenFunction) { struct Worker { - string doWork(Try&& t) { + std::string doWork(Try&& t) { return t.value() + ";class"; } - static string doWorkStatic(Try&& t) { + static std::string doWorkStatic(Try&& t) { return t.value() + ";class-static"; } } w; - auto f = makeFuture("start") + auto f = makeFuture("start") .then(doWorkStatic) .then(Worker::doWorkStatic) .then(&Worker::doWork, &w); @@ -455,21 +391,21 @@ TEST(Future, thenFunction) { EXPECT_EQ(f.value(), "start;static;class-static;class"); } -static Future doWorkStaticFuture(Try&& t) { +static Future doWorkStaticFuture(Try&& t) { return makeFuture(t.value() + ";static"); } TEST(Future, thenFunctionFuture) { struct Worker { - Future doWorkFuture(Try&& t) { + Future doWorkFuture(Try&& t) { return makeFuture(t.value() + ";class"); } - static Future doWorkStaticFuture(Try&& t) { + static Future doWorkStaticFuture(Try&& t) { return makeFuture(t.value() + ";class-static"); } } w; - auto f = makeFuture("start") + auto f = makeFuture("start") .then(doWorkStaticFuture) .then(Worker::doWorkStaticFuture) .then(&Worker::doWorkFuture, &w); @@ -487,17 +423,17 @@ TEST(Future, thenBind) { } TEST(Future, thenBindTry) { - auto l = [](Try&& t) { + auto l = [](Try&& t) { return makeFuture(t.value() + ";bind"); }; auto b = std::bind(l, std::placeholders::_1); - auto f = makeFuture("start").then(std::move(b)); + auto f = makeFuture("start").then(std::move(b)); EXPECT_EQ(f.value(), "start;bind"); } TEST(Future, value) { - auto f = makeFuture(unique_ptr(new int(42))); + auto f = makeFuture(std::unique_ptr(new int(42))); auto up = std::move(f.value()); EXPECT_EQ(42, *up); @@ -536,103 +472,16 @@ TEST(Future, makeFuture) { EXPECT_EQ(42, makeFuture(42).value()); auto fun = [] { return 42; }; - EXPECT_TYPE(makeFutureTry(fun), Future); - EXPECT_EQ(42, makeFutureTry(fun).value()); + EXPECT_TYPE(makeFutureWith(fun), Future); + EXPECT_EQ(42, makeFutureWith(fun).value()); auto failfun = []() -> int { throw eggs; }; - EXPECT_TYPE(makeFutureTry(failfun), Future); - EXPECT_THROW(makeFutureTry(failfun).value(), eggs_t); + EXPECT_TYPE(makeFutureWith(failfun), Future); + EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t); EXPECT_TYPE(makeFuture(), Future); } -// Promise - -TEST(Promise, special) { - EXPECT_FALSE(std::is_copy_constructible>::value); - EXPECT_FALSE(std::is_copy_assignable>::value); - EXPECT_TRUE(std::is_move_constructible>::value); - EXPECT_TRUE(std::is_move_assignable>::value); -} - -TEST(Promise, getFuture) { - Promise p; - Future f = p.getFuture(); - EXPECT_FALSE(f.isReady()); -} - -TEST(Promise, setValue) { - Promise fund; - auto ffund = fund.getFuture(); - fund.setValue(42); - EXPECT_EQ(42, ffund.value()); - - struct Foo { - string name; - int value; - }; - - Promise pod; - auto fpod = pod.getFuture(); - Foo f = {"the answer", 42}; - pod.setValue(f); - Foo f2 = fpod.value(); - EXPECT_EQ(f.name, f2.name); - EXPECT_EQ(f.value, f2.value); - - pod = Promise(); - fpod = pod.getFuture(); - pod.setValue(std::move(f2)); - Foo f3 = fpod.value(); - EXPECT_EQ(f.name, f3.name); - EXPECT_EQ(f.value, f3.value); - - Promise> mov; - auto fmov = mov.getFuture(); - mov.setValue(unique_ptr(new int(42))); - unique_ptr ptr = std::move(fmov.value()); - EXPECT_EQ(42, *ptr); - - Promise v; - auto fv = v.getFuture(); - v.setValue(); - EXPECT_TRUE(fv.isReady()); -} - -TEST(Promise, setException) { - { - Promise p; - auto f = p.getFuture(); - p.setException(eggs); - EXPECT_THROW(f.value(), eggs_t); - } - { - Promise p; - auto f = p.getFuture(); - try { - throw eggs; - } catch (...) { - p.setException(exception_wrapper(std::current_exception())); - } - EXPECT_THROW(f.value(), eggs_t); - } -} - -TEST(Promise, setWith) { - { - Promise p; - auto f = p.getFuture(); - p.setWith([] { return 42; }); - EXPECT_EQ(42, f.value()); - } - { - Promise p; - auto f = p.getFuture(); - p.setWith([]() -> int { throw eggs; }); - EXPECT_THROW(f.value(), eggs_t); - } -} - TEST(Future, finish) { auto x = std::make_shared(0); { @@ -690,431 +539,6 @@ TEST(Future, unwrap) { EXPECT_EQ(7, f.value()); } -TEST(Future, collectAll) { - // returns a vector variant - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collectAll(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()); - auto& results = allf.value(); - for (auto& t : results) { - EXPECT_EQ(42, t.value()); - } - } - - // check error semantics - { - vector> promises(4); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collectAll(futures.begin(), futures.end()); - - - promises[0].setValue(42); - promises[1].setException(eggs); - - EXPECT_FALSE(allf.isReady()); - - promises[2].setValue(42); - - EXPECT_FALSE(allf.isReady()); - - promises[3].setException(eggs); - - EXPECT_TRUE(allf.isReady()); - EXPECT_FALSE(allf.getTry().hasException()); - - auto& results = allf.value(); - EXPECT_EQ(42, results[0].value()); - EXPECT_TRUE(results[1].hasException()); - EXPECT_EQ(42, results[2].value()); - EXPECT_TRUE(results[3].hasException()); - } - - // check that futures are ready in then() - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collectAll(futures.begin(), futures.end()) - .then([](Try>>&& ts) { - for (auto& f : ts.value()) - f.value(); - }); - - random_shuffle(promises.begin(), promises.end()); - for (auto& p : promises) - p.setValue(); - EXPECT_TRUE(allf.isReady()); - } -} - -TEST(Future, collect) { - // success case - { - vector> promises(10); - vector> 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> promises(10); - vector> 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> promises(10); - vector> 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> promises(10); - vector> 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); - } - - // move only compiles - { - vector>> promises(10); - vector>> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - collect(futures.begin(), futures.end()); - } - -} - -struct NotDefaultConstructible { - NotDefaultConstructible() = delete; - NotDefaultConstructible(int arg) : i(arg) {} - int i; -}; - -// We have a specialized implementation for non-default-constructible objects -// Ensure that it works and preserves order -TEST(Future, collectNotDefaultConstructible) { - vector> promises(10); - vector> futures; - vector indices(10); - std::iota(indices.begin(), indices.end(), 0); - random_shuffle(indices.begin(), indices.end()); - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = collect(futures.begin(), futures.end()); - - for (auto i : indices) { - EXPECT_FALSE(allf.isReady()); - promises[i].setValue(NotDefaultConstructible(i)); - } - - EXPECT_TRUE(allf.isReady()); - int i = 0; - for (auto val : allf.value()) { - EXPECT_EQ(i, val.i); - i++; - } -} - -TEST(Future, collectAny) { - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - for (auto& f : futures) { - EXPECT_FALSE(f.isReady()); - } - - auto anyf = collectAny(futures.begin(), futures.end()); - - /* futures were moved in, so these are invalid now */ - EXPECT_FALSE(anyf.isReady()); - - promises[7].setValue(42); - EXPECT_TRUE(anyf.isReady()); - auto& idx_fut = anyf.value(); - - auto i = idx_fut.first; - EXPECT_EQ(7, i); - - auto& f = idx_fut.second; - EXPECT_EQ(42, f.value()); - } - - // error - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - for (auto& f : futures) { - EXPECT_FALSE(f.isReady()); - } - - auto anyf = collectAny(futures.begin(), futures.end()); - - EXPECT_FALSE(anyf.isReady()); - - promises[3].setException(eggs); - EXPECT_TRUE(anyf.isReady()); - EXPECT_TRUE(anyf.value().second.hasException()); - } - - // then() - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto anyf = collectAny(futures.begin(), futures.end()) - .then([](pair> p) { - EXPECT_EQ(42, p.second.value()); - }); - - promises[3].setValue(42); - EXPECT_TRUE(anyf.isReady()); - } -} - - -TEST(when, already_completed) { - { - vector> fs; - for (int i = 0; i < 10; i++) - fs.push_back(makeFuture()); - - collectAll(fs.begin(), fs.end()) - .then([&](vector> ts) { - EXPECT_EQ(fs.size(), ts.size()); - }); - } - { - vector> fs; - for (int i = 0; i < 10; i++) - fs.push_back(makeFuture(i)); - - collectAny(fs.begin(), fs.end()) - .then([&](pair> p) { - EXPECT_EQ(p.first, p.second.value()); - }); - } -} - -TEST(when, collectN) { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - bool flag = false; - size_t n = 3; - collectN(futures.begin(), futures.end(), n) - .then([&](vector>> v) { - flag = true; - EXPECT_EQ(n, v.size()); - for (auto& tt : v) - EXPECT_TRUE(tt.second.hasValue()); - }); - - promises[0].setValue(); - EXPECT_FALSE(flag); - promises[1].setValue(); - EXPECT_FALSE(flag); - promises[2].setValue(); - EXPECT_TRUE(flag); -} - -/* Ensure that we can compile when_{all,any} with folly::small_vector */ -TEST(when, small_vector) { - - static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future), - "Futures should not be trivially copyable"); - static_assert(!FOLLY_IS_TRIVIALLY_COPYABLE(Future), - "Futures should not be trivially copyable"); - - using folly::small_vector; - { - small_vector> futures; - - for (int i = 0; i < 10; i++) - futures.push_back(makeFuture()); - - auto anyf = collectAny(futures.begin(), futures.end()); - } - - { - small_vector> futures; - - for (int i = 0; i < 10; i++) - futures.push_back(makeFuture()); - - auto allf = collectAll(futures.begin(), futures.end()); - } -} - -TEST(Future, collectAllVariadic) { - Promise pb; - Promise pi; - Future fb = pb.getFuture(); - Future fi = pi.getFuture(); - bool flag = false; - collectAll(std::move(fb), std::move(fi)) - .then([&](std::tuple, Try> tup) { - flag = true; - EXPECT_TRUE(std::get<0>(tup).hasValue()); - EXPECT_EQ(std::get<0>(tup).value(), true); - EXPECT_TRUE(std::get<1>(tup).hasValue()); - EXPECT_EQ(std::get<1>(tup).value(), 42); - }); - pb.setValue(true); - EXPECT_FALSE(flag); - pi.setValue(42); - EXPECT_TRUE(flag); -} - -TEST(Future, collectAllVariadicReferences) { - Promise pb; - Promise pi; - Future fb = pb.getFuture(); - Future fi = pi.getFuture(); - bool flag = false; - collectAll(fb, fi) - .then([&](std::tuple, Try> tup) { - flag = true; - EXPECT_TRUE(std::get<0>(tup).hasValue()); - EXPECT_EQ(std::get<0>(tup).value(), true); - EXPECT_TRUE(std::get<1>(tup).hasValue()); - EXPECT_EQ(std::get<1>(tup).value(), 42); - }); - pb.setValue(true); - EXPECT_FALSE(flag); - pi.setValue(42); - EXPECT_TRUE(flag); -} - -TEST(Future, collectAll_none) { - vector> fs; - auto f = collectAll(fs.begin(), fs.end()); - EXPECT_TRUE(f.isReady()); -} - TEST(Future, throwCaughtInImmediateThen) { // Neither of these should throw "Promise already satisfied" makeFuture().then( @@ -1143,260 +567,13 @@ TEST(Future, throwIfFailed) { }); } -TEST(Future, waitImmediate) { - makeFuture().wait(); - auto done = makeFuture(42).wait().value(); - EXPECT_EQ(42, done); - - vector v{1,2,3}; - auto done_v = makeFuture(v).wait().value(); - EXPECT_EQ(v.size(), done_v.size()); - EXPECT_EQ(v, done_v); - - vector> v_f; - v_f.push_back(makeFuture()); - v_f.push_back(makeFuture()); - auto done_v_f = collectAll(v_f.begin(), v_f.end()).wait().value(); - EXPECT_EQ(2, done_v_f.size()); - - vector> v_fb; - v_fb.push_back(makeFuture(true)); - v_fb.push_back(makeFuture(false)); - auto fut = collectAll(v_fb.begin(), v_fb.end()); - auto done_v_fb = std::move(fut.wait().value()); - EXPECT_EQ(2, done_v_fb.size()); -} - -TEST(Future, wait) { - Promise p; - Future f = p.getFuture(); - std::atomic flag{false}; - std::atomic result{1}; - std::atomic id; - - std::thread t([&](Future&& tf){ - auto n = tf.then([&](Try && t) { - id = std::this_thread::get_id(); - return t.value(); - }); - flag = true; - result.store(n.wait().value()); - }, - std::move(f) - ); - while(!flag){} - EXPECT_EQ(result.load(), 1); - p.setValue(42); - t.join(); - // validate that the callback ended up executing in this thread, which - // is more to ensure that this test actually tests what it should - EXPECT_EQ(id, std::this_thread::get_id()); - EXPECT_EQ(result.load(), 42); -} - -struct MoveFlag { - MoveFlag() = default; - MoveFlag(const MoveFlag&) = delete; - MoveFlag(MoveFlag&& other) noexcept { - other.moved = true; - } - bool moved{false}; -}; - -TEST(Future, waitReplacesSelf) { - // wait - { - // lvalue - auto f1 = makeFuture(MoveFlag()); - f1.wait(); - EXPECT_FALSE(f1.value().moved); - - // rvalue - auto f2 = makeFuture(MoveFlag()).wait(); - EXPECT_FALSE(f2.value().moved); - } - - // wait(Duration) - { - // lvalue - auto f1 = makeFuture(MoveFlag()); - f1.wait(milliseconds(1)); - EXPECT_FALSE(f1.value().moved); - - // rvalue - auto f2 = makeFuture(MoveFlag()).wait(milliseconds(1)); - EXPECT_FALSE(f2.value().moved); - } - - // waitVia - { - folly::EventBase eb; - // lvalue - auto f1 = makeFuture(MoveFlag()); - f1.waitVia(&eb); - EXPECT_FALSE(f1.value().moved); - - // rvalue - auto f2 = makeFuture(MoveFlag()).waitVia(&eb); - EXPECT_FALSE(f2.value().moved); - } -} - -TEST(Future, waitWithDuration) { - { - Promise p; - Future f = p.getFuture(); - f.wait(milliseconds(1)); - EXPECT_FALSE(f.isReady()); - p.setValue(1); - EXPECT_TRUE(f.isReady()); - } - { - Promise p; - Future f = p.getFuture(); - p.setValue(1); - f.wait(milliseconds(1)); - EXPECT_TRUE(f.isReady()); - } - { - vector> v_fb; - v_fb.push_back(makeFuture(true)); - v_fb.push_back(makeFuture(false)); - auto f = collectAll(v_fb.begin(), v_fb.end()); - f.wait(milliseconds(1)); - EXPECT_TRUE(f.isReady()); - EXPECT_EQ(2, f.value().size()); - } - { - vector> v_fb; - Promise p1; - Promise p2; - v_fb.push_back(p1.getFuture()); - v_fb.push_back(p2.getFuture()); - auto f = collectAll(v_fb.begin(), v_fb.end()); - f.wait(milliseconds(1)); - EXPECT_FALSE(f.isReady()); - p1.setValue(true); - EXPECT_FALSE(f.isReady()); - p2.setValue(true); - EXPECT_TRUE(f.isReady()); - } - { - auto f = makeFuture().wait(milliseconds(1)); - EXPECT_TRUE(f.isReady()); - } - - { - Promise p; - auto start = std::chrono::steady_clock::now(); - auto f = p.getFuture().wait(milliseconds(100)); - auto elapsed = std::chrono::steady_clock::now() - start; - EXPECT_GE(elapsed, milliseconds(100)); - EXPECT_FALSE(f.isReady()); - p.setValue(); - EXPECT_TRUE(f.isReady()); - } - - { - // Try to trigger the race where the resultant Future is not yet complete - // even if we didn't hit the timeout, and make sure we deal with it properly - Promise p; - folly::Baton<> b; - auto t = std::thread([&]{ - b.post(); - /* sleep override */ std::this_thread::sleep_for(milliseconds(100)); - p.setValue(); - }); - b.wait(); - auto f = p.getFuture().wait(std::chrono::seconds(3600)); - EXPECT_TRUE(f.isReady()); - t.join(); - } -} - -class DummyDrivableExecutor : public DrivableExecutor { - public: - void add(Func f) override {} - void drive() override { ran = true; } - bool ran{false}; -}; - -TEST(Future, getVia) { - { - // non-void - ManualExecutor x; - auto f = via(&x).then([]{ return true; }); - EXPECT_TRUE(f.getVia(&x)); - } - - { - // void - ManualExecutor x; - auto f = via(&x).then(); - f.getVia(&x); - } - - { - DummyDrivableExecutor x; - auto f = makeFuture(true); - EXPECT_TRUE(f.getVia(&x)); - EXPECT_FALSE(x.ran); - } -} - -TEST(Future, waitVia) { - { - ManualExecutor x; - auto f = via(&x).then(); - EXPECT_FALSE(f.isReady()); - f.waitVia(&x); - EXPECT_TRUE(f.isReady()); - } - - { - // try rvalue as well - ManualExecutor x; - auto f = via(&x).then().waitVia(&x); - EXPECT_TRUE(f.isReady()); - } - - { - DummyDrivableExecutor x; - makeFuture(true).waitVia(&x); - EXPECT_FALSE(x.ran); - } -} - -TEST(Future, viaRaces) { - ManualExecutor x; - Promise p; - auto tid = std::this_thread::get_id(); - bool done = false; - - std::thread t1([&] { - p.getFuture() - .via(&x) - .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) - .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) - .then([&](Try&&) { done = true; }); - }); - - std::thread t2([&] { - p.setValue(); - }); - - while (!done) x.run(); - t1.join(); - t2.join(); -} - -TEST(Future, getFuture_after_setValue) { +TEST(Future, getFutureAfterSetValue) { Promise p; p.setValue(42); EXPECT_EQ(42, p.getFuture().value()); } -TEST(Future, getFuture_after_setException) { +TEST(Future, getFutureAfterSetException) { Promise p; p.setWith([]() -> void { throw std::logic_error("foo"); }); EXPECT_THROW(p.getFuture().value(), std::logic_error); @@ -1425,72 +602,6 @@ TEST(Future, detachRace) { t1.join(); } -class TestData : public RequestData { - public: - explicit TestData(int data) : data_(data) {} - virtual ~TestData() {} - int data_; -}; - -TEST(Future, context) { - - // Start a new context - RequestContext::create(); - - EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); - - // Set some test data - RequestContext::get()->setContextData( - "test", - std::unique_ptr(new TestData(10))); - - // Start a future - Promise p; - auto future = p.getFuture().then([&]{ - // Check that the context followed the future - EXPECT_TRUE(RequestContext::get() != nullptr); - auto a = dynamic_cast( - RequestContext::get()->getContextData("test")); - auto data = a->data_; - EXPECT_EQ(10, data); - }); - - // Clear the context - RequestContext::setContext(nullptr); - - EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); - - // Fulfill the promise - p.setValue(); -} - - -// This only fails about 1 in 1k times when the bug is present :( -TEST(Future, t5506504) { - ThreadExecutor x; - - auto fn = [&x]{ - auto promises = std::make_shared>>(4); - vector> futures; - - for (auto& p : *promises) { - futures.emplace_back( - p.getFuture() - .via(&x) - .then([](Try&&){})); - } - - x.waitForStartup(); - x.add([promises]{ - for (auto& p : *promises) p.setValue(); - }); - - return collectAll(futures.begin(), futures.end()); - }; - - fn().wait(); -} - // Test of handling of a circular dependency. It's never recommended // to have one because of possible memory leaks. Here we test that // we can handle freeing of the Future while it is running. @@ -1543,239 +654,3 @@ TEST(Future, thenDynamic) { p.setValue(2); EXPECT_EQ(f.get(), 5); } - -TEST(Future, via_then_get_was_racy) { - ThreadExecutor x; - std::unique_ptr val = folly::via(&x) - .then([] { return folly::make_unique(42); }) - .get(); - ASSERT_TRUE(!!val); - EXPECT_EQ(42, *val); -} - -TEST(Future, ensure) { - size_t count = 0; - auto cob = [&]{ count++; }; - auto f = makeFuture(42) - .ensure(cob) - .then([](int) { throw std::runtime_error("ensure"); }) - .ensure(cob); - - EXPECT_THROW(f.get(), std::runtime_error); - EXPECT_EQ(2, count); -} - -TEST(Future, willEqual) { - //both p1 and p2 already fulfilled - { - Promise p1; - Promise p2; - p1.setValue(27); - p2.setValue(27); - auto f1 = p1.getFuture(); - auto f2 = p2.getFuture(); - EXPECT_TRUE(f1.willEqual(f2).get()); - }{ - Promise p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 p1; - Promise 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 encapsulated_future = makeFuture(5484); - Future> 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 = 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 becomes ready once the promise -// is fulfilled. -TEST(Future, Unwrap_FutureNotReady) { - Promise> p; - Future> future = p.getFuture(); - Future 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> fs; - for (int i = 1; i <= count; ++i) { - fs.emplace_back(makeFuture(i)); - } - return fs; - }; - - // Empty (Try) - { - auto fs = makeFutures(0); - - Future f1 = reduce(fs.begin(), fs.end(), 1.2, - [](double a, Try&& b){ - return a + *b + 0.1; - }); - EXPECT_EQ(1.2, f1.get()); - } - - // One (Try) - { - auto fs = makeFutures(1); - - Future f1 = reduce(fs.begin(), fs.end(), 0.0, - [](double a, Try&& b){ - return a + *b + 0.1; - }); - EXPECT_EQ(1.1, f1.get()); - } - - // Returning values (Try) - { - auto fs = makeFutures(3); - - Future f1 = reduce(fs.begin(), fs.end(), 0.0, - [](double a, Try&& b){ - return a + *b + 0.1; - }); - EXPECT_EQ(6.3, f1.get()); - } - - // Returning values - { - auto fs = makeFutures(3); - - Future 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 f2 = reduce(fs.begin(), fs.end(), 0.0, - [](double a, Try&& b){ - return makeFuture(a + *b + 0.1); - }); - EXPECT_EQ(6.3, f2.get()); - } - - // Returning futures - { - auto fs = makeFutures(3); - - Future f2 = reduce(fs.begin(), fs.end(), 0.0, - [](double a, int&& b){ - return makeFuture(a + b + 0.1); - }); - EXPECT_EQ(6.3, f2.get()); - } -} - -TEST(Map, Basic) { - Promise p1; - Promise p2; - Promise p3; - - std::vector> fs; - fs.push_back(p1.getFuture()); - fs.push_back(p2.getFuture()); - fs.push_back(p3.getFuture()); - - int c = 0; - auto fs2 = futures::map(fs.begin(), fs.end(), [&](int i){ - c += i; - }); - - // Ensure we call the callbacks as the futures complete regardless of order - p2.setValue(1); - EXPECT_EQ(1, c); - p3.setValue(1); - EXPECT_EQ(2, c); - p1.setValue(1); - EXPECT_EQ(3, c); - - EXPECT_TRUE(collect(fs2.begin(), fs2.end()).isReady()); -}