OLD | NEW |
| (Empty) |
1 // Copyright 2015 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/edk/util/waitable_event.h" | |
6 | |
7 #include <errno.h> | |
8 #include <time.h> | |
9 | |
10 #include "mojo/edk/util/logging_internal.h" | |
11 | |
12 namespace mojo { | |
13 namespace util { | |
14 | |
15 namespace { | |
16 | |
17 // Returns the number of microseconds elapsed since epoch start (according to a | |
18 // monotonic clock). | |
19 uint64_t Now() { | |
20 const uint64_t kMicrosecondsPerSecond = 1000000ULL; | |
21 const uint64_t kNanosecondsPerMicrosecond = 1000ULL; | |
22 | |
23 struct timespec now; | |
24 int error = clock_gettime(CLOCK_MONOTONIC, &now); | |
25 INTERNAL_DCHECK_WITH_ERRNO(!error, "clock_gettime", errno); | |
26 INTERNAL_DCHECK(now.tv_sec >= 0); | |
27 INTERNAL_DCHECK(now.tv_nsec >= 0); | |
28 | |
29 return static_cast<uint64_t>(now.tv_sec) * kMicrosecondsPerSecond + | |
30 static_cast<uint64_t>(now.tv_nsec) / kNanosecondsPerMicrosecond; | |
31 } | |
32 | |
33 // Waits with a timeout on |condition()|. Returns true on timeout, or false if | |
34 // |condition()| ever returns true. |condition()| should have no side effects | |
35 // (and will always be called with |*mutex| held). | |
36 template <typename ConditionFn> | |
37 bool WaitWithTimeoutImpl(Mutex* mutex, | |
38 CondVar* cv, | |
39 ConditionFn condition, | |
40 uint64_t timeout_microseconds) | |
41 MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex) { | |
42 mutex->AssertHeld(); | |
43 | |
44 if (condition()) | |
45 return false; | |
46 | |
47 // We may get spurious wakeups. | |
48 uint64_t wait_remaining = timeout_microseconds; | |
49 uint64_t start = Now(); | |
50 while (true) { | |
51 if (cv->WaitWithTimeout(mutex, wait_remaining)) | |
52 return true; // Definitely timed out. | |
53 | |
54 // We may have been awoken. | |
55 if (condition()) | |
56 return false; | |
57 | |
58 // Or the wakeup may have been spurious. | |
59 uint64_t now = Now(); | |
60 INTERNAL_DCHECK(now >= start); | |
61 uint64_t elapsed = now - start; | |
62 // It's possible that we may have timed out anyway. | |
63 if (elapsed >= timeout_microseconds) | |
64 return true; | |
65 | |
66 // Otherwise, recalculate the amount that we have left to wait. | |
67 wait_remaining = timeout_microseconds - elapsed; | |
68 } | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 // AutoResetWaitableEvent ------------------------------------------------------ | |
74 | |
75 void AutoResetWaitableEvent::Signal() { | |
76 MutexLocker locker(&mutex_); | |
77 signaled_ = true; | |
78 cv_.Signal(); | |
79 } | |
80 | |
81 void AutoResetWaitableEvent::Reset() { | |
82 MutexLocker locker(&mutex_); | |
83 signaled_ = false; | |
84 } | |
85 | |
86 void AutoResetWaitableEvent::Wait() { | |
87 MutexLocker locker(&mutex_); | |
88 while (!signaled_) | |
89 cv_.Wait(&mutex_); | |
90 signaled_ = false; | |
91 } | |
92 | |
93 bool AutoResetWaitableEvent::WaitWithTimeout(uint64_t timeout_microseconds) { | |
94 MutexLocker locker(&mutex_); | |
95 | |
96 if (signaled_) { | |
97 signaled_ = false; | |
98 return false; | |
99 } | |
100 | |
101 // We may get spurious wakeups. | |
102 uint64_t wait_remaining = timeout_microseconds; | |
103 uint64_t start = Now(); | |
104 while (true) { | |
105 if (cv_.WaitWithTimeout(&mutex_, wait_remaining)) | |
106 return true; // Definitely timed out. | |
107 | |
108 // We may have been awoken. | |
109 if (signaled_) | |
110 break; | |
111 | |
112 // Or the wakeup may have been spurious. | |
113 uint64_t now = Now(); | |
114 INTERNAL_DCHECK(now >= start); | |
115 uint64_t elapsed = now - start; | |
116 // It's possible that we may have timed out anyway. | |
117 if (elapsed >= timeout_microseconds) | |
118 return true; | |
119 | |
120 // Otherwise, recalculate the amount that we have left to wait. | |
121 wait_remaining = timeout_microseconds - elapsed; | |
122 } | |
123 | |
124 signaled_ = false; | |
125 return false; | |
126 } | |
127 | |
128 bool AutoResetWaitableEvent::IsSignaledForTest() { | |
129 MutexLocker locker(&mutex_); | |
130 return signaled_; | |
131 } | |
132 | |
133 // ManualResetWaitableEvent ---------------------------------------------------- | |
134 | |
135 void ManualResetWaitableEvent::Signal() { | |
136 MutexLocker locker(&mutex_); | |
137 signaled_ = true; | |
138 signal_id_++; | |
139 cv_.SignalAll(); | |
140 } | |
141 | |
142 void ManualResetWaitableEvent::Reset() { | |
143 MutexLocker locker(&mutex_); | |
144 signaled_ = false; | |
145 } | |
146 | |
147 void ManualResetWaitableEvent::Wait() { | |
148 MutexLocker locker(&mutex_); | |
149 | |
150 if (signaled_) | |
151 return; | |
152 | |
153 auto last_signal_id = signal_id_; | |
154 do { | |
155 cv_.Wait(&mutex_); | |
156 } while (signal_id_ == last_signal_id); | |
157 } | |
158 | |
159 bool ManualResetWaitableEvent::WaitWithTimeout(uint64_t timeout_microseconds) { | |
160 MutexLocker locker(&mutex_); | |
161 | |
162 auto last_signal_id = signal_id_; | |
163 // Disable thread-safety analysis for the lambda: We could annotate it with | |
164 // |MOJO_EXCLUSIVE_LOCKS_REQUIRED(mutex_)|, but then the analyzer currently | |
165 // isn't able to figure out that |WaitWithTimeoutImpl()| calls it while | |
166 // holding |mutex_|. | |
167 bool rv = WaitWithTimeoutImpl( | |
168 &mutex_, &cv_, [this, last_signal_id]() MOJO_NO_THREAD_SAFETY_ANALYSIS { | |
169 // Also check |signaled_| in case we're already signaled. | |
170 return signaled_ || signal_id_ != last_signal_id; | |
171 }, timeout_microseconds); | |
172 INTERNAL_DCHECK(rv || signaled_ || signal_id_ != last_signal_id); | |
173 return rv; | |
174 } | |
175 | |
176 bool ManualResetWaitableEvent::IsSignaledForTest() { | |
177 MutexLocker locker(&mutex_); | |
178 return signaled_; | |
179 } | |
180 | |
181 } // namespace util | |
182 } // namespace mojo | |
OLD | NEW |