blob: c05d038e0e45e49d1980da91a41fabf17388a614 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/edk/system/waitable_event.h"
#include "base/logging.h"
#include "base/time/time.h"
using mojo::util::CondVar;
using mojo::util::Mutex;
using mojo::util::MutexLocker;
namespace mojo {
namespace system {
namespace {
// Waits with a timeout on |condition()|. Returns true on timeout, or false if
// |condition()| ever returns true. |condition()| should have no side effects
// (and will always be called with |*mutex| held).
template <typename ConditionFn>
bool WaitWithTimeoutImpl(Mutex* mutex,
CondVar* cv,
ConditionFn condition,
uint64_t timeout_microseconds)
MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
mutex->AssertHeld();
if (condition())
return false;
// We may get spurious wakeups.
uint64_t wait_remaining = timeout_microseconds;
auto start = base::TimeTicks::Now();
while (true) {
if (cv->WaitWithTimeout(mutex, wait_remaining))
return true; // Definitely timed out.
// We may have been awoken.
if (condition())
return false;
// Or the wakeup may have been spurious.
auto now = base::TimeTicks::Now();
DCHECK_GE(now, start);
uint64_t elapsed = static_cast<uint64_t>((now - start).InMicroseconds());
// It's possible that we may have timed out anyway.
if (elapsed >= timeout_microseconds)
return true;
// Otherwise, recalculate the amount that we have left to wait.
wait_remaining = timeout_microseconds - elapsed;
}
}
} // namespace
// AutoResetWaitableEvent ------------------------------------------------------
void AutoResetWaitableEvent::Signal() {
MutexLocker locker(&mutex_);
signaled_ = true;
cv_.Signal();
}
void AutoResetWaitableEvent::Reset() {
MutexLocker locker(&mutex_);
signaled_ = false;
}
void AutoResetWaitableEvent::Wait() {
MutexLocker locker(&mutex_);
while (!signaled_)
cv_.Wait(&mutex_);
signaled_ = false;
}
bool AutoResetWaitableEvent::WaitWithTimeout(uint64_t timeout_microseconds) {
MutexLocker locker(&mutex_);
if (signaled_) {
signaled_ = false;
return false;
}
// We may get spurious wakeups.
uint64_t wait_remaining = timeout_microseconds;
auto start = base::TimeTicks::Now();
while (true) {
if (cv_.WaitWithTimeout(&mutex_, wait_remaining))
return true; // Definitely timed out.
// We may have been awoken.
if (signaled_)
break;
// Or the wakeup may have been spurious.
auto now = base::TimeTicks::Now();
DCHECK_GE(now, start);
uint64_t elapsed = static_cast<uint64_t>((now - start).InMicroseconds());
// It's possible that we may have timed out anyway.
if (elapsed >= timeout_microseconds)
return true;
// Otherwise, recalculate the amount that we have left to wait.
wait_remaining = timeout_microseconds - elapsed;
}
signaled_ = false;
return false;
}
bool AutoResetWaitableEvent::IsSignaledForTest() {
MutexLocker locker(&mutex_);
return signaled_;
}
// ManualResetWaitableEvent ----------------------------------------------------
void ManualResetWaitableEvent::Signal() {
MutexLocker locker(&mutex_);
signaled_ = true;
signal_id_++;
cv_.SignalAll();
}
void ManualResetWaitableEvent::Reset() {
MutexLocker locker(&mutex_);
signaled_ = false;
}
void ManualResetWaitableEvent::Wait() {
MutexLocker locker(&mutex_);
if (signaled_)
return;
auto last_signal_id = signal_id_;
do {
cv_.Wait(&mutex_);
} while (signal_id_ == last_signal_id);
}
bool ManualResetWaitableEvent::WaitWithTimeout(uint64_t timeout_microseconds) {
MutexLocker locker(&mutex_);
auto last_signal_id = signal_id_;
// Disable thread-safety analysis for the lambda: We could annotate it with
// |MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_)|, but then the analyzer currently
// isn't able to figure out that |WaitWithTimeoutImpl()| calls it while
// holding |mutex_|.
bool rv = WaitWithTimeoutImpl(
&mutex_, &cv_, [this, last_signal_id]() MOJO_NO_THREAD_SAFETY_ANALYSIS {
// Also check |signaled_| in case we're already signaled.
return signaled_ || signal_id_ != last_signal_id;
}, timeout_microseconds);
DCHECK(rv || signaled_ || signal_id_ != last_signal_id);
return rv;
}
bool ManualResetWaitableEvent::IsSignaledForTest() {
MutexLocker locker(&mutex_);
return signaled_;
}
} // namespace system
} // namespace mojo