| Index: base/synchronization/condition_variable_win.cc
|
| diff --git a/base/synchronization/condition_variable_win.cc b/base/synchronization/condition_variable_win.cc
|
| index 8bece6eb34dbfc965ccee65b08051c54ad9fff79..17675d07dd2aa4d172bdcb12dc2c8c9a0ab715a5 100644
|
| --- a/base/synchronization/condition_variable_win.cc
|
| +++ b/base/synchronization/condition_variable_win.cc
|
| @@ -10,6 +10,57 @@
|
|
|
| namespace base {
|
|
|
| +namespace {
|
| +
|
| +// Infinite timeout in milliseconds.
|
| +static const int64_t kInfiniteTimeout =
|
| + TimeDelta::FromMilliseconds(INFINITE).InMilliseconds();
|
| +
|
| +// Number of tries after SleepConditionVariableSRW() fails.
|
| +static const int kMaxNumberOfTriesAfterFailure = 10;
|
| +
|
| +// As per MSDN documentation (https://goo.gl/tPHPkY), condition variables are
|
| +// subject to spurious wakeups (those not associated with an explicit wake) and
|
| +// stolen wakeups (another thread manages to run before the woken thread). This
|
| +// function checks whether it's a real timeout and if not it retries
|
| +// kMaxNumberOfTriesAfterFailure times after which it crashes.
|
| +void SleepConditionVariable(PCONDITION_VARIABLE cv,
|
| + PSRWLOCK srw_lock,
|
| + BOOL infinite_timeout,
|
| + DWORD timeout,
|
| + ULONG flags) {
|
| + base::Time time_start = base::Time::Now();
|
| + int num_tries_after_failure = 0;
|
| +
|
| + while (!SleepConditionVariableSRW(cv, srw_lock, timeout, flags)) {
|
| + // Note: Use ERROR_TIMEOUT with GetLastError().
|
| + DWORD error = GetLastError();
|
| +
|
| + num_tries_after_failure++;
|
| + CHECK_LE(num_tries_after_failure, kMaxNumberOfTriesAfterFailure)
|
| + << "SleepConditionVariableSRW() failed after max number of retries "
|
| + "with error code ("
|
| + << error << ").";
|
| +
|
| + if (infinite_timeout) {
|
| + // Ideally for infinite timeout, we should not get ERROR_TIMEOUT error.
|
| + DCHECK_NE(error, static_cast<DWORD>(ERROR_TIMEOUT));
|
| + } else {
|
| + if (error == ERROR_TIMEOUT)
|
| + break;
|
| +
|
| + // Compute remaining timeout.
|
| + int64_t time_delta = (base::Time::Now() - time_start).InMilliseconds();
|
| + timeout -= time_delta;
|
| + // Break here as if timeout has occurred, otherwise continue to wait
|
| + // for the remaining timeout.
|
| + if (timeout <= 0)
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +}
|
| +
|
| ConditionVariable::ConditionVariable(Lock* user_lock)
|
| : srwlock_(user_lock->lock_.native_handle())
|
| #if DCHECK_IS_ON()
|
| @@ -23,25 +74,28 @@ ConditionVariable::ConditionVariable(Lock* user_lock)
|
| ConditionVariable::~ConditionVariable() = default;
|
|
|
| void ConditionVariable::Wait() {
|
| - TimedWait(TimeDelta::FromMilliseconds(INFINITE));
|
| + base::ThreadRestrictions::AssertWaitAllowed();
|
| +
|
| +#if DCHECK_IS_ON()
|
| + user_lock_->CheckHeldAndUnmark();
|
| +#endif
|
| +
|
| + SleepConditionVariable(&cv_, srwlock_, true, kInfiniteTimeout, 0);
|
| +
|
| +#if DCHECK_IS_ON()
|
| + user_lock_->CheckUnheldAndMark();
|
| +#endif
|
| }
|
|
|
| void ConditionVariable::TimedWait(const TimeDelta& max_time) {
|
| base::ThreadRestrictions::AssertWaitAllowed();
|
| - DWORD timeout = static_cast<DWORD>(max_time.InMilliseconds());
|
|
|
| #if DCHECK_IS_ON()
|
| user_lock_->CheckHeldAndUnmark();
|
| #endif
|
|
|
| - if (!SleepConditionVariableSRW(&cv_, srwlock_, timeout, 0)) {
|
| - // On failure, we only expect the CV to timeout. Any other error value means
|
| - // that we've unexpectedly woken up.
|
| - // Note that WAIT_TIMEOUT != ERROR_TIMEOUT. WAIT_TIMEOUT is used with the
|
| - // WaitFor* family of functions as a direct return value. ERROR_TIMEOUT is
|
| - // used with GetLastError().
|
| - DCHECK_EQ(static_cast<DWORD>(ERROR_TIMEOUT), GetLastError());
|
| - }
|
| + SleepConditionVariable(&cv_, srwlock_, false,
|
| + static_cast<DWORD>(max_time.InMilliseconds()), 0);
|
|
|
| #if DCHECK_IS_ON()
|
| user_lock_->CheckUnheldAndMark();
|
|
|