// 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 <stdint.h> #include <limits> #include "base/logging.h" #include "base/time/time.h" namespace mojo { namespace edk { Waiter::Waiter() : cv_(&lock_), #if DCHECK_IS_ON() initialized_(false), #endif awoken_(false), awake_result_(MOJO_RESULT_INTERNAL), awake_context_(static_cast<uint32_t>(-1)) { } Waiter::~Waiter() { } void Waiter::Init() { #if DCHECK_IS_ON() 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, uintptr_t* context) { base::AutoLock locker(lock_); #if DCHECK_IS_ON() 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 = awake_context_; return awake_result_; } // |MojoDeadline| is actually a |uint64_t|, but we need a signed quantity. // Treat any out-of-range deadline as "forever" (which is wrong, but okay // since 2^63 microseconds is ~300000 years). Note that this also takes care // of the |MOJO_DEADLINE_INDEFINITE| (= 2^64 - 1) case. if (deadline > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) { do { cv_.Wait(); } while (!awoken_); } else { // NOTE(vtl): This is very inefficient on POSIX, since pthreads condition // variables take an absolute deadline. const base::TimeTicks end_time = base::TimeTicks::Now() + base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline)); do { base::TimeTicks now_time = base::TimeTicks::Now(); if (now_time >= end_time) return MOJO_RESULT_DEADLINE_EXCEEDED; cv_.TimedWait(end_time - now_time); } while (!awoken_); } DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL); if (context) *context = awake_context_; return awake_result_; } bool Waiter::Awake(MojoResult result, uintptr_t context) { base::AutoLock locker(lock_); if (awoken_) return true; awoken_ = true; awake_result_ = result; awake_context_ = context; cv_.Signal(); // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released. return true; } } // namespace edk } // namespace mojo