1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "mojo/system/waiter.h"
6
7#include <limits>
8
9#include "base/logging.h"
10#include "base/time/time.h"
11
12namespace mojo {
13namespace system {
14
15Waiter::Waiter()
16    : cv_(&lock_),
17#ifndef NDEBUG
18      initialized_(false),
19#endif
20      awoken_(false),
21      wait_result_(MOJO_RESULT_INTERNAL) {
22}
23
24Waiter::~Waiter() {
25}
26
27void Waiter::Init() {
28#ifndef NDEBUG
29  initialized_ = true;
30#endif
31  awoken_ = false;
32  // NOTE(vtl): If performance ever becomes an issue, we can disable the setting
33  // of |wait_result_| (except the first one in |Awake()|) in Release builds.
34  wait_result_ = MOJO_RESULT_INTERNAL;
35}
36
37// TODO(vtl): Fast-path the |deadline == 0| case?
38MojoResult Waiter::Wait(MojoDeadline deadline) {
39  base::AutoLock locker(lock_);
40
41#ifndef NDEBUG
42  DCHECK(initialized_);
43  // It'll need to be re-initialized after this.
44  initialized_ = false;
45#endif
46
47  // Fast-path the already-awoken case:
48  if (awoken_) {
49    DCHECK_NE(wait_result_, MOJO_RESULT_INTERNAL);
50    return wait_result_;
51  }
52
53  // |MojoDeadline| is actually a |uint64_t|, but we need a signed quantity.
54  // Treat any out-of-range deadline as "forever" (which is wrong, but okay
55  // since 2^63 microseconds is ~300000 years). Note that this also takes care
56  // of the |MOJO_DEADLINE_INDEFINITE| (= 2^64 - 1) case.
57  if (deadline > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
58    do {
59      cv_.Wait();
60    } while (!awoken_);
61  } else {
62    // NOTE(vtl): This is very inefficient on POSIX, since pthreads condition
63    // variables take an absolute deadline.
64    const base::TimeTicks end_time = base::TimeTicks::HighResNow() +
65        base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline));
66    do {
67      base::TimeTicks now_time = base::TimeTicks::HighResNow();
68      if (now_time >= end_time)
69        return MOJO_RESULT_DEADLINE_EXCEEDED;
70
71      cv_.TimedWait(end_time - now_time);
72    } while (!awoken_);
73  }
74
75  DCHECK_NE(wait_result_, MOJO_RESULT_INTERNAL);
76  return wait_result_;
77}
78
79void Waiter::Awake(MojoResult wait_result) {
80  base::AutoLock locker(lock_);
81
82  if (awoken_)
83    return;
84
85  awoken_ = true;
86  wait_result_ = wait_result;
87  cv_.Signal();
88  // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released.
89}
90
91}  // namespace system
92}  // namespace mojo
93