X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ffutures%2Ftest%2FFutureTest.cpp;h=5d171bb9732f50f9c0e12476072b3a79d2e9a253;hb=5180b66230ee290a22bdba66f81e9f33e0216dd9;hp=ed696f9a65c497313212bbb716f063a6a6358970;hpb=42ab246273a3c4f63b3645207818d0ea698eadc0;p=folly.git diff --git a/folly/futures/test/FutureTest.cpp b/folly/futures/test/FutureTest.cpp index ed696f9a..5d171bb9 100644 --- a/folly/futures/test/FutureTest.cpp +++ b/folly/futures/test/FutureTest.cpp @@ -14,83 +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 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::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(128, sizeof(detail::Core)); -} - // Future TEST(Future, onError) { @@ -257,26 +204,66 @@ TEST(Future, onError) { .onError([&] (eggs_t& e) { throw e; return makeFuture(-1); }); 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([]{ throw eggs; return 0; }) + .onError([&] (exception_wrapper e) { + flag(); + throw eggs; + return makeFuture(-1); + }); + EXPECT_FLAG(); + EXPECT_THROW(f.value(), eggs_t); + } - A a(5); - Try t_a(std::move(a)); + // 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()); + } - Try t_void; + // 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()); + } - EXPECT_EQ(5, t_a.value().x()); } TEST(Future, special) { @@ -287,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"); } @@ -369,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); @@ -391,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); @@ -423,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); @@ -472,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, 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); - } -} - TEST(Future, finish) { auto x = std::make_shared(0); { @@ -626,274 +539,6 @@ 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( @@ -922,262 +567,15 @@ 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 = whenAll(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 = whenAll(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 = whenAll(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 = whenAll(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.fulfil([]() -> void { throw std::logic_error("foo"); }); + p.setWith([]() -> void { throw std::logic_error("foo"); }); EXPECT_THROW(p.getFuture().value(), std::logic_error); } @@ -1204,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")); - - // Fulfil 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 whenAll(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. @@ -1291,7 +623,7 @@ TEST(Future, CircularDependencySharedPtrSelfReset) { ptr.reset(); - promise.fulfil([]{return 1l;}); + promise.setWith([]{return 1l;}); } TEST(Future, Constructor) { @@ -1309,104 +641,16 @@ TEST(Future, ImplicitConstructor) { //auto f2 = []() -> Future { }(); } -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()); - } +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); }