blob: 540dcb629870631b29a5a5172c843cf98333ae0f [file] [log] [blame]
// Copyright 2013 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/waiter.h"
#include "base/logging.h"
#include "base/time/time.h"
using mojo::util::MutexLocker;
namespace mojo {
namespace system {
Waiter::Waiter()
:
#ifndef NDEBUG
initialized_(false),
#endif
awoken_(false),
awake_result_(MOJO_RESULT_INTERNAL),
awake_context_(static_cast<uint32_t>(-1)) {
}
Waiter::~Waiter() {
}
void Waiter::Init() {
#ifndef NDEBUG
initialized_ = true;
#endif
awoken_ = false;
// NOTE(vtl): If performance ever becomes an issue, we can disable the setting
// of |awake_result_| (except the first one in |Awake()|) in Release builds.
awake_result_ = MOJO_RESULT_INTERNAL;
}
// TODO(vtl): Fast-path the |deadline == 0| case?
MojoResult Waiter::Wait(MojoDeadline deadline, uint32_t* context) {
MutexLocker locker(&mutex_);
#ifndef NDEBUG
DCHECK(initialized_);
// It'll need to be re-initialized after this.
initialized_ = false;
#endif
// Fast-path the already-awoken case:
if (awoken_) {
DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
if (context)
*context = static_cast<uint32_t>(awake_context_);
return awake_result_;
}
if (deadline == MOJO_DEADLINE_INDEFINITE) {
do {
cv_.Wait(&mutex_);
} while (!awoken_);
} else {
// We may get spurious wakeups, so record the start time and track the
// remaining timeout.
uint64_t wait_remaining = deadline;
auto start = base::TimeTicks::Now();
while (true) {
// NOTE(vtl): Possibly, we should add a version of |WaitWithTimeout()|
// that takes an absolute deadline, since that's what pthreads takes.
if (cv_.WaitWithTimeout(&mutex_, wait_remaining))
return MOJO_RESULT_DEADLINE_EXCEEDED; // Definitely timed out.
// Otherwise, we may have been awoken.
if (awoken_)
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 the deadline has passed anyway.
if (elapsed >= deadline)
return MOJO_RESULT_DEADLINE_EXCEEDED;
// Otherwise, recalculate the amount that we have left to wait.
wait_remaining = deadline - elapsed;
}
}
DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
if (context)
*context = static_cast<uint32_t>(awake_context_);
return awake_result_;
}
bool Waiter::Awake(MojoResult result, uintptr_t context) {
MutexLocker locker(&mutex_);
if (awoken_)
return true;
awoken_ = true;
awake_result_ = result;
awake_context_ = context;
cv_.Signal();
// |cv_.Wait()|/|cv_.WaitWithTimeout()| will return after |mutex_| is
// released.
return true;
}
} // namespace system
} // namespace mojo