X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ffutures%2Ftest%2FFutureTest.cpp;h=4dd3f2dc574239d9c6c5d76c5b567c0fbe6e23cb;hb=refs%2Ftags%2Fv2017.07.10.00;hp=dbe0b942e8ab9f455e0fe2e2559dd4fcdb887b3a;hpb=7887ef2afa255fc67160146ac212bbd55cc75900;p=folly.git diff --git a/folly/futures/test/FutureTest.cpp b/folly/futures/test/FutureTest.cpp index dbe0b942..4dd3f2dc 100644 --- a/folly/futures/test/FutureTest.cpp +++ b/folly/futures/test/FutureTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,73 +14,81 @@ * 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 using namespace folly; -using std::pair; -using std::string; -using std::unique_ptr; -using std::vector; #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; +typedef FutureException eggs_t; +static eggs_t eggs("eggs"); - void work() { - baton.post(); - Func fn; - while (!done) { - while (!funcs.isEmpty()) { - funcs.blockingRead(fn); - fn(); - } - } - } +// Future - public: - explicit ThreadExecutor(size_t n = 1024) - : funcs(n), worker(std::bind(&ThreadExecutor::work, this)) {} +TEST(Future, makeEmpty) { + auto f = Future::makeEmpty(); + EXPECT_THROW(f.isReady(), NoState); +} - ~ThreadExecutor() { - done = true; - funcs.write([]{}); - worker.join(); - } +TEST(Future, futureDefaultCtor) { + Future(); +} - void add(Func fn) override { - funcs.blockingWrite(std::move(fn)); - } +TEST(Future, futureToUnit) { + Future fu = makeFuture(42).unit(); + fu.value(); + EXPECT_TRUE(makeFuture(eggs).unit().hasException()); +} - void waitForStartup() { - baton.wait(); - } -}; +TEST(Future, voidFutureToUnit) { + Future fu = makeFuture().unit(); + fu.value(); + EXPECT_TRUE(makeFuture(eggs).unit().hasException()); +} -typedef FutureException eggs_t; -static eggs_t eggs("eggs"); +TEST(Future, unitFutureToUnitIdentity) { + Future fu = makeFuture(Unit{}).unit(); + fu.value(); + EXPECT_TRUE(makeFuture(eggs).unit().hasException()); +} -// Future +TEST(Future, toUnitWhileInProgress) { + Promise p; + Future fu = p.getFuture().unit(); + EXPECT_FALSE(fu.isReady()); + p.setValue(42); + EXPECT_TRUE(fu.isReady()); +} + +TEST(Future, makeFutureWithUnit) { + int count = 0; + Future fu = makeFutureWith([&] { count++; }); + EXPECT_EQ(1, count); +} + +namespace { +Future onErrorHelperEggs(const eggs_t&) { + return makeFuture(10); +} +Future onErrorHelperGeneric(const std::exception&) { + return makeFuture(20); +} +} TEST(Future, onError) { bool theFlag = false; @@ -99,173 +107,266 @@ TEST(Future, onError) { // By reference { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](eggs_t& /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](eggs_t& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // By value { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](eggs_t /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](eggs_t /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // Polymorphic { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::exception& e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](std::exception& /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::exception& e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](std::exception& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // Non-exceptions { - auto f = makeFuture() - .then([] { throw -1; }) - .onError([&] (int e) { flag(); }); + auto f = makeFuture().then([] { + throw - 1; + }).onError([&](int /* e */) { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw -1; }) - .onError([&] (int e) { flag(); return makeFuture(); }); + .then([] { throw - 1; }) + .onError([&](int /* e */) { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } // Mutable lambda { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) mutable { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](eggs_t& /* e */) mutable { flag(); }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (eggs_t& e) mutable { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](eggs_t& /* e */) mutable { + flag(); + return makeFuture(); + }); EXPECT_FLAG(); EXPECT_NO_THROW(f.value()); } + // Function pointer + { + auto f = makeFuture() + .then([]() -> int { throw eggs; }) + .onError(onErrorHelperEggs) + .onError(onErrorHelperGeneric); + EXPECT_EQ(10, f.value()); + } + { + auto f = makeFuture() + .then([]() -> int { throw std::runtime_error("test"); }) + .onError(onErrorHelperEggs) + .onError(onErrorHelperGeneric); + EXPECT_EQ(20, f.value()); + } + { + auto f = makeFuture() + .then([]() -> int { throw std::runtime_error("test"); }) + .onError(onErrorHelperEggs); + EXPECT_THROW(f.value(), std::runtime_error); + } + // No throw { auto f = makeFuture() - .then([] { return 42; }) - .onError([&] (eggs_t& e) { flag(); return -1; }); + .then([] { return 42; }) + .onError([&](eggs_t& /* e */) { + flag(); + return -1; + }); EXPECT_NO_FLAG(); EXPECT_EQ(42, f.value()); } { auto f = makeFuture() - .then([] { return 42; }) - .onError([&] (eggs_t& e) { flag(); return makeFuture(-1); }); + .then([] { return 42; }) + .onError([&](eggs_t& /* e */) { + flag(); + return makeFuture(-1); + }); EXPECT_NO_FLAG(); EXPECT_EQ(42, f.value()); } // Catch different exception { - auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::runtime_error& e) { flag(); }); + auto f = makeFuture().then([] { + throw eggs; + }).onError([&](std::runtime_error& /* e */) { flag(); }); EXPECT_NO_FLAG(); EXPECT_THROW(f.value(), eggs_t); } { auto f = makeFuture() - .then([] { throw eggs; }) - .onError([&] (std::runtime_error& e) { flag(); return makeFuture(); }); + .then([] { throw eggs; }) + .onError([&](std::runtime_error& /* e */) { + flag(); + return makeFuture(); + }); EXPECT_NO_FLAG(); EXPECT_THROW(f.value(), eggs_t); } // Returned value propagates { - auto f = makeFuture() - .then([] { throw eggs; return 0; }) - .onError([&] (eggs_t& e) { return 42; }); + auto f = makeFuture().then([]() -> int { + throw eggs; + }).onError([&](eggs_t& /* e */) { return 42; }); EXPECT_EQ(42, f.value()); } // Returned future propagates { - auto f = makeFuture() - .then([] { throw eggs; return 0; }) - .onError([&] (eggs_t& e) { return makeFuture(42); }); + auto f = makeFuture().then([]() -> int { + throw eggs; + }).onError([&](eggs_t& /* e */) { return makeFuture(42); }); EXPECT_EQ(42, f.value()); } // Throw in callback { auto f = makeFuture() - .then([] { throw eggs; return 0; }) - .onError([&] (eggs_t& e) { throw e; return -1; }); + .then([]() -> int { throw eggs; }) + .onError([&] (eggs_t& e) -> int { throw e; }); EXPECT_THROW(f.value(), eggs_t); } { auto f = makeFuture() - .then([] { throw eggs; return 0; }) - .onError([&] (eggs_t& e) { throw e; return makeFuture(-1); }); + .then([]() -> int { throw eggs; }) + .onError([&] (eggs_t& e) -> Future { throw e; }); EXPECT_THROW(f.value(), eggs_t); } -} -TEST(Future, try) { - class A { - public: - A(int x) : x_(x) {} + // exception_wrapper, return Future + { + auto f = makeFuture() + .then([] { throw eggs; }) + .onError([&](exception_wrapper /* e */) { + flag(); + return makeFuture(); + }); + EXPECT_FLAG(); + EXPECT_NO_THROW(f.value()); + } - int x() const { - return x_; - } - private: - int x_; - }; + // exception_wrapper, return Future but throw + { + auto f = makeFuture() + .then([]() -> int { + throw eggs; + }) + .onError([&](exception_wrapper /* e */) -> Future { + flag(); + throw eggs; + }); + EXPECT_FLAG(); + EXPECT_THROW(f.value(), eggs_t); + } + + // exception_wrapper, return T + { + auto f = makeFuture() + .then([]() -> int { + throw eggs; + }) + .onError([&](exception_wrapper /* e */) { + flag(); + return -1; + }); + EXPECT_FLAG(); + EXPECT_EQ(-1, f.value()); + } - A a(5); - Try t_a(std::move(a)); + // exception_wrapper, return T but throw + { + auto f = makeFuture() + .then([]() -> int { + throw eggs; + }) + .onError([&](exception_wrapper /* e */) -> int { + flag(); + throw eggs; + }); + EXPECT_FLAG(); + EXPECT_THROW(f.value(), eggs_t); + } - Try t_void; + // const exception_wrapper& + { + auto f = makeFuture() + .then([] { throw eggs; }) + .onError([&](const exception_wrapper& /* e */) { + flag(); + return makeFuture(); + }); + EXPECT_FLAG(); + EXPECT_NO_THROW(f.value()); + } - EXPECT_EQ(5, t_a.value().x()); } TEST(Future, special) { @@ -275,6 +376,38 @@ TEST(Future, special) { EXPECT_TRUE(std::is_move_assignable>::value); } +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([](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"); +} + TEST(Future, thenTry) { bool flag = false; @@ -289,11 +422,11 @@ TEST(Future, thenTry) { .then([&](Try&& t) { flag = true; EXPECT_EQ(42, t.value()); }); EXPECT_TRUE(flag); flag = false; - makeFuture().then([&](Try&& t) { flag = true; t.value(); }); + makeFuture().then([&](Try&& t) { flag = true; t.value(); }); EXPECT_TRUE(flag); flag = false; - Promise p; - auto f = p.getFuture().then([&](Try&& t) { flag = true; }); + Promise p; + auto f = p.getFuture().then([&](Try&& /* t */) { flag = true; }); EXPECT_FALSE(flag); EXPECT_FALSE(f.isReady()); p.setValue(); @@ -319,10 +452,10 @@ TEST(Future, thenValue) { }); EXPECT_TRUE(flag); flag = false; - auto f = makeFuture(eggs).then([&](int i){}); + auto f = makeFuture(eggs).then([&](int /* i */) {}); EXPECT_THROW(f.value(), eggs_t); - f = makeFuture(eggs).then([&]{}); + f = makeFuture(eggs).then([&]{}); EXPECT_THROW(f.value(), eggs_t); } @@ -333,58 +466,102 @@ TEST(Future, thenValueFuture) { .then([&](Try&& t) { flag = true; EXPECT_EQ(42, t.value()); }); EXPECT_TRUE(flag); flag = false; - makeFuture() - .then([]{ return makeFuture(); }) - .then([&](Try&& t) { flag = true; }); + makeFuture().then([] { + return makeFuture(); + }).then([&](Try&& /* t */) { flag = true; }); 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(&w, &Worker::doWork); + .then(&Worker::doWork, &w); 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(&w, &Worker::doWorkFuture); + .then(&Worker::doWorkFuture, &w); EXPECT_EQ(f.value(), "start;static;class-static;class"); } +TEST(Future, thenStdFunction) { + { + std::function fn = [](){ return 42; }; + auto f = makeFuture().then(std::move(fn)); + EXPECT_EQ(f.value(), 42); + } + { + std::function fn = [](int i){ return i + 23; }; + auto f = makeFuture(19).then(std::move(fn)); + EXPECT_EQ(f.value(), 42); + } + { + std::function&)> fn = [](Try& t){ return t.value() + 2; }; + auto f = makeFuture(1).then(std::move(fn)); + EXPECT_EQ(f.value(), 3); + } + { + bool flag = false; + std::function fn = [&flag](){ flag = true; }; + auto f = makeFuture().then(std::move(fn)); + EXPECT_TRUE(f.isReady()); + EXPECT_TRUE(flag); + } +} + +TEST(Future, thenBind) { + auto l = []() { + return makeFuture("bind"); + }; + auto b = std::bind(l); + auto f = makeFuture().then(std::move(b)); + EXPECT_EQ(f.value(), "bind"); +} + +TEST(Future, thenBindTry) { + 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)); + + 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); @@ -423,120 +600,76 @@ 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); + auto funf = [] { return makeFuture(43); }; + EXPECT_TYPE(makeFutureWith(funf), Future); + EXPECT_EQ(43, makeFutureWith(funf).value()); - EXPECT_TYPE(makeFuture(), Future); -} + auto failfun = []() -> int { throw eggs; }; + EXPECT_TYPE(makeFutureWith(failfun), Future); + EXPECT_NO_THROW(makeFutureWith(failfun)); + EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t); -// Promise + auto failfunf = []() -> Future { throw eggs; }; + EXPECT_TYPE(makeFutureWith(failfunf), Future); + EXPECT_NO_THROW(makeFutureWith(failfunf)); + EXPECT_THROW(makeFutureWith(failfunf).value(), eggs_t); -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); + EXPECT_TYPE(makeFuture(), Future); } -TEST(Promise, getFuture) { +TEST(Future, finish) { + auto x = std::make_shared(0); + Promise p; - Future f = p.getFuture(); - EXPECT_FALSE(f.isReady()); -} + auto f = p.getFuture().then([x](Try&& t) { *x = t.value(); }); -TEST(Promise, setValue) { - Promise fund; - auto ffund = fund.getFuture(); - fund.setValue(42); - EXPECT_EQ(42, ffund.value()); + // The callback hasn't executed + EXPECT_EQ(0, *x); - struct Foo { - string name; - int value; - }; + // The callback has a reference to x + EXPECT_EQ(2, x.use_count()); - 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()); -} + p.setValue(42); -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); - } -} + // the callback has executed + EXPECT_EQ(42, *x); -TEST(Promise, fulfil) { - { - Promise p; - auto f = p.getFuture(); - p.fulfil([] { return 42; }); - EXPECT_EQ(42, f.value()); - } - { - Promise p; - auto f = p.getFuture(); - p.fulfil([]() -> int { throw eggs; }); - EXPECT_THROW(f.value(), eggs_t); - } + // the callback has been destructed + // and has released its reference to x + EXPECT_EQ(1, x.use_count()); } -TEST(Future, finish) { +TEST(Future, finishBigLambda) { auto x = std::make_shared(0); - { - Promise p; - auto f = p.getFuture().then([x](Try&& t) { *x = t.value(); }); - // The callback hasn't executed - EXPECT_EQ(0, *x); + // bulk_data, to be captured in the lambda passed to Future::then. + // This is meant to force that the lambda can't be stored inside + // the Future object. + std::array)> bulk_data = {{0}}; - // The callback has a reference to x - EXPECT_EQ(2, x.use_count()); + // suppress gcc warning about bulk_data not being used + EXPECT_EQ(bulk_data[0], 0); - p.setValue(42); + Promise p; + auto f = p.getFuture().then([x, bulk_data](Try&& t) { + (void)bulk_data; + *x = t.value(); + }); + + // The callback hasn't executed + EXPECT_EQ(0, *x); + + // The callback has a reference to x + EXPECT_EQ(2, x.use_count()); + + p.setValue(42); + + // the callback has executed + EXPECT_EQ(42, *x); - // the callback has executed - EXPECT_EQ(42, *x); - } // the callback has been destructed // and has released its reference to x EXPECT_EQ(1, x.use_count()); @@ -577,289 +710,21 @@ TEST(Future, unwrap) { EXPECT_EQ(7, f.value()); } -TEST(Future, whenAll) { - // returns a vector variant - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - auto allf = whenAll(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 = whenAll(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 = whenAll(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, whenAny) { - { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - for (auto& f : futures) { - EXPECT_FALSE(f.isReady()); - } - - auto anyf = whenAny(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 = whenAny(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 = whenAny(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()); - - whenAll(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)); - - whenAny(fs.begin(), fs.end()) - .then([&](pair> p) { - EXPECT_EQ(p.first, p.second.value()); - }); - } -} - -TEST(when, whenN) { - vector> promises(10); - vector> futures; - - for (auto& p : promises) - futures.push_back(p.getFuture()); - - bool flag = false; - size_t n = 3; - whenN(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 = whenAny(futures.begin(), futures.end()); - } - - { - small_vector> futures; - - for (int i = 0; i < 10; i++) - futures.push_back(makeFuture()); - - auto allf = whenAll(futures.begin(), futures.end()); - } -} - -TEST(Future, whenAllVariadic) { - Promise pb; - Promise pi; - Future fb = pb.getFuture(); - Future fi = pi.getFuture(); - bool flag = false; - whenAll(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, whenAllVariadicReferences) { - Promise pb; - Promise pi; - Future fb = pb.getFuture(); - Future fi = pi.getFuture(); - bool flag = false; - whenAll(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, whenAll_none) { - vector> fs; - auto f = whenAll(fs.begin(), fs.end()); - EXPECT_TRUE(f.isReady()); -} - TEST(Future, throwCaughtInImmediateThen) { // Neither of these should throw "Promise already satisfied" makeFuture().then( - [=](Try&&) -> int { throw std::exception(); }); + [=](Try&&) -> int { throw std::exception(); }); makeFuture().then( - [=](Try&&) -> Future { throw std::exception(); }); + [=](Try&&) -> Future { throw std::exception(); }); } TEST(Future, throwIfFailed) { - makeFuture(eggs) - .then([=](Try&& t) { + makeFuture(eggs) + .then([=](Try&& t) { EXPECT_THROW(t.throwIfFailed(), eggs_t); }); makeFuture() - .then([=](Try&& t) { + .then([=](Try&& t) { EXPECT_NO_THROW(t.throwIfFailed()); }); @@ -873,215 +738,15 @@ TEST(Future, throwIfFailed) { }); } -TEST(Future, waitWithSemaphoreImmediate) { - waitWithSemaphore(makeFuture()); - auto done = waitWithSemaphore(makeFuture(42)).value(); - EXPECT_EQ(42, done); - - vector v{1,2,3}; - auto done_v = waitWithSemaphore(makeFuture(v)).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 = waitWithSemaphore(whenAll(v_f.begin(), v_f.end())).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 = whenAll(v_fb.begin(), v_fb.end()); - auto done_v_fb = std::move(waitWithSemaphore(std::move(fut)).value()); - EXPECT_EQ(2, done_v_fb.size()); -} - -TEST(Future, waitWithSemaphore) { - 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(waitWithSemaphore(std::move(n)).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); -} - -TEST(Future, waitWithSemaphoreForTime) { - { - Promise p; - Future f = p.getFuture(); - auto t = waitWithSemaphore(std::move(f), - std::chrono::microseconds(1)); - EXPECT_FALSE(t.isReady()); - p.setValue(1); - EXPECT_TRUE(t.isReady()); - } - { - Promise p; - Future f = p.getFuture(); - p.setValue(1); - auto t = waitWithSemaphore(std::move(f), - std::chrono::milliseconds(1)); - EXPECT_TRUE(t.isReady()); - } - { - vector> v_fb; - v_fb.push_back(makeFuture(true)); - v_fb.push_back(makeFuture(false)); - auto f = whenAll(v_fb.begin(), v_fb.end()); - auto t = waitWithSemaphore(std::move(f), - std::chrono::milliseconds(1)); - EXPECT_TRUE(t.isReady()); - EXPECT_EQ(2, t.value().size()); - } - { - vector> v_fb; - Promise p1; - Promise p2; - v_fb.push_back(p1.getFuture()); - v_fb.push_back(p2.getFuture()); - auto f = whenAll(v_fb.begin(), v_fb.end()); - auto t = waitWithSemaphore(std::move(f), - std::chrono::milliseconds(1)); - EXPECT_FALSE(t.isReady()); - p1.setValue(true); - EXPECT_FALSE(t.isReady()); - p2.setValue(true); - EXPECT_TRUE(t.isReady()); - } - { - auto t = waitWithSemaphore(makeFuture(), - std::chrono::milliseconds(1)); - EXPECT_TRUE(t.isReady()); - } -} - -TEST(Future, callbackAfterActivate) { - Promise p; - auto f = p.getFuture(); - f.deactivate(); - - size_t count = 0; - f.then([&](Try&&) { count++; }); - - p.setValue(); - EXPECT_EQ(0, count); - - f.activate(); - EXPECT_EQ(1, count); -} - -TEST(Future, activateOnDestruct) { - auto f = std::make_shared>(makeFuture()); - f->deactivate(); - - size_t count = 0; - f->then([&](Try&&) { count++; }); - EXPECT_EQ(0, count); - - f.reset(); - EXPECT_EQ(1, count); -} - -TEST(Future, viaActsCold) { - ManualExecutor x; - size_t count = 0; - - auto fv = via(&x); - fv.then([&](Try&&) { count++; }); - - EXPECT_EQ(0, count); - - fv.activate(); - - EXPECT_EQ(1, x.run()); - EXPECT_EQ(1, count); -} - -TEST(Future, viaIsCold) { - ManualExecutor x; - EXPECT_FALSE(via(&x).isActive()); -} - -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(); -} - -// TODO(#4920689) -TEST(Future, viaRaces_2stage) { - ManualExecutor x; - Promise p; - auto tid = std::this_thread::get_id(); - bool done = false; - - std::thread t1([&] { - auto f2 = p.getFuture().via(&x); - f2.then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) - .then([&](Try&&) { EXPECT_EQ(tid, std::this_thread::get_id()); }) - .then([&](Try&&) { done = true; }); - - // the bug was in the promise being fulfilled before f2 is reactivated. we - // could sleep, but yielding should cause this to fail with reasonable - // probability - std::this_thread::yield(); - f2.activate(); - }); - - 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) { - Promise p; - p.fulfil([]() -> void { throw std::logic_error("foo"); }); +TEST(Future, getFutureAfterSetException) { + Promise p; + p.setWith([]() -> void { throw std::logic_error("foo"); }); EXPECT_THROW(p.getFuture().value(), std::logic_error); } @@ -1096,8 +761,8 @@ TEST(Future, detachRace) { // slow test so I won't do that but if it ever fails, take it seriously, and // run the test binary with "--gtest_repeat=10000 --gtest_filter=*detachRace" // (Don't forget to enable ASAN) - auto p = folly::make_unique>(); - auto f = folly::make_unique>(p->getFuture()); + auto p = std::make_unique>(); + auto f = std::make_unique>(p->getFuture()); folly::Baton<> baton; std::thread t1([&]{ baton.post(); @@ -1108,92 +773,166 @@ 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); +// 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. +TEST(Future, CircularDependencySharedPtrSelfReset) { + Promise promise; + auto ptr = std::make_shared>(promise.getFuture()); + + ptr->then([ptr](folly::Try&& /* uid */) mutable { + EXPECT_EQ(1, ptr.use_count()); + + // Leaving no references to ourselves. + ptr.reset(); + EXPECT_EQ(0, ptr.use_count()); }); - // Clear the context - RequestContext::setContext(nullptr); + EXPECT_EQ(2, ptr.use_count()); - EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test")); + ptr.reset(); - // Fulfil the promise - p.setValue(); + promise.setValue(1); } +TEST(Future, Constructor) { + auto f1 = []() -> Future { return Future(3); }(); + EXPECT_EQ(f1.value(), 3); + auto f2 = []() -> Future { return Future(); }(); + EXPECT_NO_THROW(f2.value()); +} -// This only fails about 1 in 1k times when the bug is present :( -TEST(Future, t5506504) { - ThreadExecutor x; +TEST(Future, ImplicitConstructor) { + auto f1 = []() -> Future { return 3; }(); + EXPECT_EQ(f1.value(), 3); + // Unfortunately, the C++ standard does not allow the + // following implicit conversion to work: + //auto f2 = []() -> Future { }(); +} - auto fn = [&x]{ - auto promises = std::make_shared>>(4); - vector> futures; +TEST(Future, InPlaceConstructor) { + auto f = Future>(in_place, 5, 3.2); + EXPECT_EQ(5, f.value().first); +} - for (auto& p : *promises) { - futures.emplace_back( - p.getFuture() - .via(&x) - .then([](Try&&){})); +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 because that then fails to compile + Promise p; + Future f = p.getFuture().then( + [](const folly::dynamic& d) { + return folly::dynamic(d.asInt() + 3); + } + ); + p.setValue(2); + EXPECT_EQ(f.get(), 5); +} + +TEST(Future, RequestContext) { + class NewThreadExecutor : public Executor { + public: + ~NewThreadExecutor() override { + std::for_each(v_.begin(), v_.end(), [](std::thread& t){ t.join(); }); + } + void add(Func f) override { + if (throwsOnAdd_) { throw std::exception(); } + v_.emplace_back(std::move(f)); } + void addWithPriority(Func f, int8_t /* prio */) override { + add(std::move(f)); + } + uint8_t getNumPriorities() const override { return numPriorities_; } - x.waitForStartup(); - x.add([promises]{ - for (auto& p : *promises) p.setValue(); - }); + void setHandlesPriorities() { numPriorities_ = 2; } + void setThrowsOnAdd() { throwsOnAdd_ = true; } + private: + std::vector v_; + uint8_t numPriorities_ = 1; + bool throwsOnAdd_ = false; + }; - return whenAll(futures.begin(), futures.end()); + struct MyRequestData : RequestData { + MyRequestData(bool value = false) : value(value) {} + bool value; }; - waitWithSemaphore(fn()); -} + Promise p1, p2; + NewThreadExecutor e; + { + folly::RequestContextScopeGuard rctx; + RequestContext::get()->setContextData( + "key", std::make_unique(true)); + auto checker = [](int lineno) { + return [lineno](Try&& /* t */) { + auto d = static_cast( + RequestContext::get()->getContextData("key")); + EXPECT_TRUE(d && d->value) << "on line " << lineno; + }; + }; -// 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. -TEST(Future, CircularDependencySharedPtrSelfReset) { - Promise promise; - auto ptr = std::make_shared>(promise.getFuture()); + makeFuture(1).via(&e).then(checker(__LINE__)); + + e.setHandlesPriorities(); + makeFuture(2).via(&e).then(checker(__LINE__)); + + p1.getFuture().then(checker(__LINE__)); - ptr->then( - [ptr] (folly::Try&& uid) mutable { - EXPECT_EQ(1, ptr.use_count()); + e.setThrowsOnAdd(); + p2.getFuture().via(&e).then(checker(__LINE__)); + } + // Assert that no RequestContext is set + EXPECT_FALSE(RequestContext::saveContext()); + p1.setValue(3); + p2.setValue(4); +} - // Leaving no references to ourselves. - ptr.reset(); - EXPECT_EQ(0, ptr.use_count()); +TEST(Future, makeFutureNoThrow) { + makeFuture().value(); +} + +TEST(Future, invokeCallbackReturningValueAsRvalue) { + struct Foo { + int operator()(int x) & { + return x + 1; } - ); + int operator()(int x) const& { + return x + 2; + } + int operator()(int x) && { + return x + 3; + } + }; - EXPECT_EQ(2, ptr.use_count()); + Foo foo; + Foo const cfoo; - ptr.reset(); + // The callback will be copied when given as lvalue or const ref, and moved + // if provided as rvalue. Either way, it should be executed as rvalue. + EXPECT_EQ(103, makeFuture(100).then(foo).value()); + EXPECT_EQ(203, makeFuture(200).then(cfoo).value()); + EXPECT_EQ(303, makeFuture(300).then(Foo()).value()); +} + +TEST(Future, invokeCallbackReturningFutureAsRvalue) { + struct Foo { + Future operator()(int x) & { + return x + 1; + } + Future operator()(int x) const& { + return x + 2; + } + Future operator()(int x) && { + return x + 3; + } + }; + + Foo foo; + Foo const cfoo; - promise.fulfil([]{return 1l;}); + // The callback will be copied when given as lvalue or const ref, and moved + // if provided as rvalue. Either way, it should be executed as rvalue. + EXPECT_EQ(103, makeFuture(100).then(foo).value()); + EXPECT_EQ(203, makeFuture(200).then(cfoo).value()); + EXPECT_EQ(303, makeFuture(300).then(Foo()).value()); }