Commit badc3ebe authored by Alexander Shaposhnikov's avatar Alexander Shaposhnikov Committed by facebook-github-bot-9

Remove busy wait and support multiple wait

Summary: Remove busy wait from Future::wait.
If future.wait(timeout) has succeded we should
return a ready future, otherwise we should return a future
for the final result (not necessarily ready).

Reviewed By: yfeldblum

Differential Revision: D2646860

fb-gh-sync-id: 62671d09073ad86e84df8c9257e961d2a8c2a339
parent 587e4b4d
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <cassert>
#include <chrono> #include <chrono>
#include <random> #include <random>
#include <thread> #include <thread>
#include <folly/experimental/fibers/Baton.h> #include <folly/experimental/fibers/Baton.h>
#include <folly/Optional.h> #include <folly/Optional.h>
#include <folly/Random.h> #include <folly/Random.h>
...@@ -936,18 +936,9 @@ void waitImpl(Future<T>& f) { ...@@ -936,18 +936,9 @@ void waitImpl(Future<T>& f) {
if (f.isReady()) return; if (f.isReady()) return;
folly::fibers::Baton baton; folly::fibers::Baton baton;
f = f.then([&](Try<T> t) { f.setCallback_([&](const Try<T>& t) { baton.post(); });
baton.post();
return makeFuture(std::move(t));
});
baton.wait(); baton.wait();
assert(f.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.
while (!f.isReady()) {
std::this_thread::yield();
}
} }
template <class T> template <class T>
...@@ -955,19 +946,16 @@ void waitImpl(Future<T>& f, Duration dur) { ...@@ -955,19 +946,16 @@ void waitImpl(Future<T>& f, Duration dur) {
// short-circuit if there's nothing to do // short-circuit if there's nothing to do
if (f.isReady()) return; if (f.isReady()) return;
folly::MoveWrapper<Promise<T>> promise;
auto ret = promise->getFuture();
auto baton = std::make_shared<folly::fibers::Baton>(); auto baton = std::make_shared<folly::fibers::Baton>();
f = f.then([baton](Try<T> t) { f.setCallback_([baton, promise](Try<T>&& t) mutable {
promise->setTry(std::move(t));
baton->post(); baton->post();
return makeFuture(std::move(t));
}); });
f = std::move(ret);
// 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(dur)) { if (baton->timed_wait(dur)) {
while (!f.isReady()) { assert(f.isReady());
std::this_thread::yield();
}
} }
} }
......
...@@ -195,3 +195,16 @@ TEST(Wait, waitWithDuration) { ...@@ -195,3 +195,16 @@ TEST(Wait, waitWithDuration) {
t.join(); t.join();
} }
} }
TEST(Wait, multipleWait) {
auto f = futures::sleep(milliseconds(100));
for (size_t i = 0; i < 5; ++i) {
EXPECT_FALSE(f.isReady());
f.wait(milliseconds(3));
}
EXPECT_FALSE(f.isReady());
f.wait();
EXPECT_TRUE(f.isReady());
f.wait();
EXPECT_TRUE(f.isReady());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment