Commit f1f9cb87 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Merge wait and timed-wait implementations in Baton

Summary:
[Folly] Merge wait and timed-wait implementations in `Baton`.

Now `MemoryIdler` is also used in the timed-wait case, not just in the wait case.

Reviewed By: spalamarchuk

Differential Revision: D6720048

fbshipit-source-id: 7612eb60d1908d08d41897fa163b0693b59abab1
parent 4f622004
...@@ -171,7 +171,8 @@ class Baton { ...@@ -171,7 +171,8 @@ class Baton {
return; return;
} }
waitSlow(opt); auto const deadline = std::chrono::steady_clock::time_point::max();
tryWaitSlow(deadline, opt);
} }
/// Similar to wait, but doesn't block the thread if it hasn't been posted. /// Similar to wait, but doesn't block the thread if it hasn't been posted.
...@@ -208,8 +209,8 @@ class Baton { ...@@ -208,8 +209,8 @@ class Baton {
return true; return true;
} }
auto deadline = std::chrono::steady_clock::now() + timeout; auto const deadline = std::chrono::steady_clock::now() + timeout;
return tryWaitUntilSlow(deadline, opt); return tryWaitSlow(deadline, opt);
} }
/// Similar to wait, but with a deadline. The thread is unblocked if the /// Similar to wait, but with a deadline. The thread is unblocked if the
...@@ -231,7 +232,7 @@ class Baton { ...@@ -231,7 +232,7 @@ class Baton {
return true; return true;
} }
return tryWaitUntilSlow(deadline, opt); return tryWaitSlow(deadline, opt);
} }
/// Alias to try_wait_for. Deprecated. /// Alias to try_wait_for. Deprecated.
...@@ -292,18 +293,27 @@ class Baton { ...@@ -292,18 +293,27 @@ class Baton {
} }
} }
FOLLY_NOINLINE void waitSlow(const WaitOptions& opt) noexcept { template <typename Clock, typename Duration>
auto const deadline = std::chrono::steady_clock::time_point::max(); FOLLY_NOINLINE bool tryWaitSlow(
const std::chrono::time_point<Clock, Duration>& deadline,
const WaitOptions& opt) noexcept {
auto const forever = std::chrono::time_point<Clock, Duration>::max();
if (spinWaitForEarlyDelivery(deadline, opt)) { if (spinWaitForEarlyDelivery(deadline, opt)) {
assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY); assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY);
return; return true;
} }
if (!MayBlock) { if (!MayBlock) {
while (!try_wait()) { while (true) {
if (try_wait()) {
return true;
}
if (deadline != forever && Clock::now() >= deadline) {
return false;
}
std::this_thread::yield(); std::this_thread::yield();
} }
return;
} }
// guess we have to block :( // guess we have to block :(
...@@ -311,12 +321,22 @@ class Baton { ...@@ -311,12 +321,22 @@ class Baton {
if (!state_.compare_exchange_strong(expected, WAITING)) { if (!state_.compare_exchange_strong(expected, WAITING)) {
// CAS failed, last minute reprieve // CAS failed, last minute reprieve
assert(expected == EARLY_DELIVERY); assert(expected == EARLY_DELIVERY);
return; return true;
} }
while (true) { while (true) {
detail::MemoryIdler::futexWait(state_, WAITING); auto rv = detail::MemoryIdler::futexWaitUntil(state_, WAITING, deadline);
// Awoken by the deadline passing.
if (rv == detail::FutexResult::TIMEDOUT) {
assert(deadline != forever);
state_.store(TIMED_OUT, std::memory_order_release);
return false;
}
// Probably awoken by a matching wake event, but could also by awoken
// by an asynchronous signal or by a spurious wakeup.
//
// state_ is the truth even if FUTEX_WAIT reported a matching // state_ is the truth even if FUTEX_WAIT reported a matching
// FUTEX_WAKE, since we aren't using type-stable storage and we // FUTEX_WAKE, since we aren't using type-stable storage and we
// don't guarantee reuse. The scenario goes like this: thread // don't guarantee reuse. The scenario goes like this: thread
...@@ -334,52 +354,6 @@ class Baton { ...@@ -334,52 +354,6 @@ class Baton {
// It would be possible to add an extra state_ dance to communicate // It would be possible to add an extra state_ dance to communicate
// that the futexWake has been sent so that we can be sure to consume // that the futexWake has been sent so that we can be sure to consume
// it before returning, but that would be a perf and complexity hit. // it before returning, but that would be a perf and complexity hit.
uint32_t s = state_.load(std::memory_order_acquire);
assert(s == WAITING || s == LATE_DELIVERY);
if (s == LATE_DELIVERY) {
return;
}
// retry
}
}
template <typename Clock, typename Duration>
FOLLY_NOINLINE bool tryWaitUntilSlow(
const std::chrono::time_point<Clock, Duration>& deadline,
const WaitOptions& opt) noexcept {
if (spinWaitForEarlyDelivery(deadline, opt)) {
assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY);
return true;
}
if (!MayBlock) {
while (true) {
if (try_wait()) {
return true;
}
if (Clock::now() >= deadline) {
return false;
}
std::this_thread::yield();
}
}
// guess we have to block :(
uint32_t expected = INIT;
if (!state_.compare_exchange_strong(expected, WAITING)) {
// CAS failed, last minute reprieve
assert(expected == EARLY_DELIVERY);
return true;
}
while (true) {
auto rv = state_.futexWaitUntil(WAITING, deadline);
if (rv == folly::detail::FutexResult::TIMEDOUT) {
state_.store(TIMED_OUT, std::memory_order_release);
return false;
}
uint32_t s = state_.load(std::memory_order_acquire); uint32_t s = state_.load(std::memory_order_acquire);
assert(s == WAITING || s == LATE_DELIVERY); assert(s == WAITING || s == LATE_DELIVERY);
if (s == LATE_DELIVERY) { if (s == LATE_DELIVERY) {
......
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