| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <gtest/gtest.h> |
| |
| #include <folly/futures/Future.h> |
| #include <folly/futures/ManualExecutor.h> |
| #include <folly/Baton.h> |
| |
| using std::vector; |
| using std::chrono::milliseconds; |
| |
| using folly::Future; |
| using folly::ManualExecutor; |
| using folly::Promise; |
| using folly::Try; |
| using folly::Unit; |
| using folly::collectAll; |
| using folly::makeFuture; |
| |
| TEST(Wait, waitImmediate) { |
| makeFuture().wait(); |
| auto done = makeFuture(42).wait().value(); |
| EXPECT_EQ(42, done); |
| |
| vector<int> v{1,2,3}; |
| auto done_v = makeFuture(v).wait().value(); |
| EXPECT_EQ(v.size(), done_v.size()); |
| EXPECT_EQ(v, done_v); |
| |
| vector<Future<Unit>> v_f; |
| v_f.push_back(makeFuture()); |
| v_f.push_back(makeFuture()); |
| auto done_v_f = collectAll(v_f).wait().value(); |
| EXPECT_EQ(2, done_v_f.size()); |
| |
| vector<Future<bool>> v_fb; |
| v_fb.push_back(makeFuture(true)); |
| v_fb.push_back(makeFuture(false)); |
| auto fut = collectAll(v_fb); |
| auto done_v_fb = std::move(fut.wait().value()); |
| EXPECT_EQ(2, done_v_fb.size()); |
| } |
| |
| TEST(Wait, wait) { |
| Promise<int> p; |
| Future<int> f = p.getFuture(); |
| std::atomic<bool> flag{false}; |
| std::atomic<int> result{1}; |
| std::atomic<std::thread::id> id; |
| |
| std::thread t([&](Future<int>&& tf){ |
| auto n = tf.then([&](Try<int> && 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& operator=(const MoveFlag&) = delete; |
| MoveFlag(const MoveFlag&) = delete; |
| MoveFlag(MoveFlag&& other) noexcept { |
| other.moved = true; |
| } |
| bool moved{false}; |
| }; |
| |
| TEST(Wait, 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 |
| { |
| ManualExecutor me; |
| // lvalue |
| auto f1 = makeFuture(MoveFlag()); |
| f1.waitVia(&me); |
| EXPECT_FALSE(f1.value().moved); |
| |
| // rvalue |
| auto f2 = makeFuture(MoveFlag()).waitVia(&me); |
| EXPECT_FALSE(f2.value().moved); |
| } |
| } |
| |
| TEST(Wait, waitWithDuration) { |
| { |
| Promise<int> p; |
| Future<int> f = p.getFuture(); |
| f.wait(milliseconds(1)); |
| EXPECT_FALSE(f.isReady()); |
| p.setValue(1); |
| EXPECT_TRUE(f.isReady()); |
| } |
| { |
| Promise<int> p; |
| Future<int> f = p.getFuture(); |
| p.setValue(1); |
| f.wait(milliseconds(1)); |
| EXPECT_TRUE(f.isReady()); |
| } |
| { |
| vector<Future<bool>> v_fb; |
| v_fb.push_back(makeFuture(true)); |
| v_fb.push_back(makeFuture(false)); |
| auto f = collectAll(v_fb); |
| f.wait(milliseconds(1)); |
| EXPECT_TRUE(f.isReady()); |
| EXPECT_EQ(2, f.value().size()); |
| } |
| { |
| vector<Future<bool>> v_fb; |
| Promise<bool> p1; |
| Promise<bool> p2; |
| v_fb.push_back(p1.getFuture()); |
| v_fb.push_back(p2.getFuture()); |
| auto f = collectAll(v_fb); |
| 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<Unit> 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<Unit> 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(); |
| } |
| } |