summaryrefslogtreecommitdiffstats
path: root/mojo/system/waiter.cc
blob: 754ece130ea9551561c912ab62badbfe2195ceb2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// 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/system/waiter.h"

#include <limits>

#include "base/logging.h"
#include "base/time/time.h"

namespace mojo {
namespace system {

Waiter::Waiter()
    : cv_(&lock_),
#ifndef NDEBUG
      initialized_(false),
#endif
      awoken_(false),
      wait_result_(MOJO_RESULT_INTERNAL) {
}

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 |wait_result_| (except the first one in |Awake()|) in Release builds.
  wait_result_ = MOJO_RESULT_INTERNAL;
}

// TODO(vtl): Fast-path the |deadline == 0| case?
MojoResult Waiter::Wait(MojoDeadline deadline) {
  base::AutoLock locker(lock_);

#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(wait_result_, MOJO_RESULT_INTERNAL);
    return wait_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::HighResNow() +
        base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline));
    do {
      base::TimeTicks now_time = base::TimeTicks::HighResNow();
      if (now_time >= end_time)
        return MOJO_RESULT_DEADLINE_EXCEEDED;

      cv_.TimedWait(end_time - now_time);
    } while (!awoken_);
  }

  DCHECK_NE(wait_result_, MOJO_RESULT_INTERNAL);
  return wait_result_;
}

void Waiter::Awake(MojoResult wait_result) {
  base::AutoLock locker(lock_);

  if (awoken_)
    return;

  awoken_ = true;
  wait_result_ = wait_result;
  cv_.Signal();
  // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released.
}

}  // namespace system
}  // namespace mojo