return ctx->p.getFuture();
}
-template <typename T>
-Future<T>
-waitWithSemaphore(Future<T>&& f) {
- Baton<> baton;
- auto done = f.then([&](Try<T> &&t) {
- baton.post();
- return std::move(t.value());
- });
- baton.wait();
- while (!done.isReady()) {
- // There's a race here between the return here and the actual finishing of
- // the future. f is completed, but the setup may not have finished on done
- // after the baton has posted.
- std::this_thread::yield();
- }
- return done;
-}
-
-template<>
-inline Future<void> waitWithSemaphore<void>(Future<void>&& f) {
- Baton<> baton;
- auto done = f.then([&](Try<void> &&t) {
- baton.post();
- t.value();
- });
- baton.wait();
- while (!done.isReady()) {
- // There's a race here between the return here and the actual finishing of
- // the future. f is completed, but the setup may not have finished on done
- // after the baton has posted.
- std::this_thread::yield();
- }
- return done;
-}
-
-template <typename T, class Dur>
-Future<T>
-waitWithSemaphore(Future<T>&& f, Dur timeout) {
- auto baton = std::make_shared<Baton<>>();
- auto done = f.then([baton](Try<T> &&t) {
- baton->post();
- return std::move(t.value());
- });
- baton->timed_wait(std::chrono::system_clock::now() + timeout);
- return done;
-}
-
-template <class Dur>
-Future<void>
-waitWithSemaphore(Future<void>&& f, Dur timeout) {
- auto baton = std::make_shared<Baton<>>();
- auto done = f.then([baton](Try<void> &&t) {
- baton->post();
- t.value();
- });
- baton->timed_wait(std::chrono::system_clock::now() + timeout);
- return done;
-}
-
namespace {
template <class T>
void getWaitHelper(Future<T>* f) {
baton->post();
return makeFuture(std::move(t));
});
- baton->timed_wait(std::chrono::system_clock::now() + dur);
+ // Let's preserve the invariant that if we did not timeout (timed_wait returns
+ // true), then the returned Future is complete when it is returned to the
+ // caller. We need to wait out the race for that Future to complete.
+ if (baton->timed_wait(std::chrono::system_clock::now() + dur)) {
+ while (!done.isReady()) {
+ std::this_thread::yield();
+ }
+ }
return done;
}
});
}
-TEST(Future, waitWithSemaphoreImmediate) {
- waitWithSemaphore(makeFuture());
- auto done = waitWithSemaphore(makeFuture(42)).value();
+TEST(Future, waitImmediate) {
+ makeFuture().wait();
+ auto done = makeFuture(42).wait().value();
EXPECT_EQ(42, done);
vector<int> v{1,2,3};
- auto done_v = waitWithSemaphore(makeFuture(v)).value();
+ auto done_v = makeFuture(v).wait().value();
EXPECT_EQ(v.size(), done_v.size());
EXPECT_EQ(v, done_v);
vector<Future<void>> 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();
+ auto done_v_f = whenAll(v_f.begin(), v_f.end()).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 = whenAll(v_fb.begin(), v_fb.end());
- auto done_v_fb = std::move(waitWithSemaphore(std::move(fut)).value());
+ auto done_v_fb = std::move(fut.wait().value());
EXPECT_EQ(2, done_v_fb.size());
}
-TEST(Future, waitWithSemaphore) {
+TEST(Future, wait) {
Promise<int> p;
Future<int> f = p.getFuture();
std::atomic<bool> flag{false};
return t.value();
});
flag = true;
- result.store(waitWithSemaphore(std::move(n)).value());
+ result.store(n.wait().value());
},
std::move(f)
);
EXPECT_EQ(result.load(), 42);
}
-TEST(Future, waitWithSemaphoreForTime) {
+TEST(Future, waitWithDuration) {
{
Promise<int> p;
Future<int> f = p.getFuture();
- auto t = waitWithSemaphore(std::move(f),
- std::chrono::microseconds(1));
+ auto t = f.wait(std::chrono::milliseconds(1));
EXPECT_FALSE(t.isReady());
p.setValue(1);
EXPECT_TRUE(t.isReady());
Promise<int> p;
Future<int> f = p.getFuture();
p.setValue(1);
- auto t = waitWithSemaphore(std::move(f),
- std::chrono::milliseconds(1));
+ auto t = f.wait(std::chrono::milliseconds(1));
EXPECT_TRUE(t.isReady());
}
{
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));
+ auto t = f.wait(std::chrono::milliseconds(1));
EXPECT_TRUE(t.isReady());
EXPECT_EQ(2, t.value().size());
}
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));
+ auto t = f.wait(std::chrono::milliseconds(1));
EXPECT_FALSE(t.isReady());
p1.setValue(true);
EXPECT_FALSE(t.isReady());
EXPECT_TRUE(t.isReady());
}
{
- auto t = waitWithSemaphore(makeFuture(),
- std::chrono::milliseconds(1));
+ auto t = makeFuture().wait(std::chrono::milliseconds(1));
EXPECT_TRUE(t.isReady());
}
+
+ {
+ Promise<void> p;
+ auto start = std::chrono::steady_clock::now();
+ auto f = p.getFuture().wait(std::chrono::milliseconds(100));
+ auto elapsed = std::chrono::steady_clock::now() - start;
+ EXPECT_GE(elapsed, std::chrono::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<void> p;
+ folly::Baton<> b;
+ auto t = std::thread([&]{
+ b.post();
+ std::this_thread::sleep_for(std::chrono::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 {
return whenAll(futures.begin(), futures.end());
};
- waitWithSemaphore(fn());
+ fn().wait();
}
// Test of handling of a circular dependency. It's never recommended