blob: 19c33f8b1f75d11493ea611f103065bb99012eb3 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// Copyright 2013 the V8 project 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 "src/base/platform/condition-variable.h"
6
7#include <errno.h>
8#include <time.h>
9
10#include "src/base/platform/time.h"
11
12namespace v8 {
13namespace base {
14
15#if V8_OS_POSIX
16
17ConditionVariable::ConditionVariable() {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000018#if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000019 (V8_OS_LINUX && V8_LIBC_GLIBC))
Ben Murdochb8a8cc12014-11-26 15:28:44 +000020 // On Free/Net/OpenBSD and Linux with glibc we can change the time
21 // source for pthread_cond_timedwait() to use the monotonic clock.
22 pthread_condattr_t attr;
23 int result = pthread_condattr_init(&attr);
24 DCHECK_EQ(0, result);
25 result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
26 DCHECK_EQ(0, result);
27 result = pthread_cond_init(&native_handle_, &attr);
28 DCHECK_EQ(0, result);
29 result = pthread_condattr_destroy(&attr);
30#else
31 int result = pthread_cond_init(&native_handle_, NULL);
32#endif
33 DCHECK_EQ(0, result);
34 USE(result);
35}
36
37
38ConditionVariable::~ConditionVariable() {
Ben Murdochc5610432016-08-08 18:44:38 +010039#if defined(V8_OS_MACOSX)
40 // This hack is necessary to avoid a fatal pthreads subsystem bug in the
41 // Darwin kernel. http://crbug.com/517681.
42 {
43 Mutex lock;
44 LockGuard<Mutex> l(&lock);
45 struct timespec ts;
46 ts.tv_sec = 0;
47 ts.tv_nsec = 1;
48 pthread_cond_timedwait_relative_np(&native_handle_, &lock.native_handle(),
49 &ts);
50 }
51#endif
Ben Murdochb8a8cc12014-11-26 15:28:44 +000052 int result = pthread_cond_destroy(&native_handle_);
53 DCHECK_EQ(0, result);
54 USE(result);
55}
56
57
58void ConditionVariable::NotifyOne() {
59 int result = pthread_cond_signal(&native_handle_);
60 DCHECK_EQ(0, result);
61 USE(result);
62}
63
64
65void ConditionVariable::NotifyAll() {
66 int result = pthread_cond_broadcast(&native_handle_);
67 DCHECK_EQ(0, result);
68 USE(result);
69}
70
71
72void ConditionVariable::Wait(Mutex* mutex) {
73 mutex->AssertHeldAndUnmark();
74 int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
75 DCHECK_EQ(0, result);
76 USE(result);
77 mutex->AssertUnheldAndMark();
78}
79
80
81bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
82 struct timespec ts;
83 int result;
84 mutex->AssertHeldAndUnmark();
85#if V8_OS_MACOSX
86 // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
87 // not depend on the real time clock, which is what you really WANT here!
88 ts = rel_time.ToTimespec();
89 DCHECK_GE(ts.tv_sec, 0);
90 DCHECK_GE(ts.tv_nsec, 0);
91 result = pthread_cond_timedwait_relative_np(
92 &native_handle_, &mutex->native_handle(), &ts);
93#else
Ben Murdochb8a8cc12014-11-26 15:28:44 +000094#if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000095 (V8_OS_LINUX && V8_LIBC_GLIBC))
Ben Murdochb8a8cc12014-11-26 15:28:44 +000096 // On Free/Net/OpenBSD and Linux with glibc we can change the time
97 // source for pthread_cond_timedwait() to use the monotonic clock.
98 result = clock_gettime(CLOCK_MONOTONIC, &ts);
99 DCHECK_EQ(0, result);
100 Time now = Time::FromTimespec(ts);
101#else
102 // The timeout argument to pthread_cond_timedwait() is in absolute time.
103 Time now = Time::NowFromSystemTime();
104#endif
105 Time end_time = now + rel_time;
106 DCHECK_GE(end_time, now);
107 ts = end_time.ToTimespec();
108 result = pthread_cond_timedwait(
109 &native_handle_, &mutex->native_handle(), &ts);
110#endif // V8_OS_MACOSX
111 mutex->AssertUnheldAndMark();
112 if (result == ETIMEDOUT) {
113 return false;
114 }
115 DCHECK_EQ(0, result);
116 return true;
117}
118
119#elif V8_OS_WIN
120
121struct ConditionVariable::Event {
122 Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
123 DCHECK(handle_ != NULL);
124 }
125
126 ~Event() {
127 BOOL ok = ::CloseHandle(handle_);
128 DCHECK(ok);
129 USE(ok);
130 }
131
132 bool WaitFor(DWORD timeout_ms) {
133 DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
134 if (result == WAIT_OBJECT_0) {
135 return true;
136 }
137 DCHECK(result == WAIT_TIMEOUT);
138 return false;
139 }
140
141 HANDLE handle_;
142 Event* next_;
143 HANDLE thread_;
144 volatile bool notified_;
145};
146
147
148ConditionVariable::NativeHandle::~NativeHandle() {
149 DCHECK(waitlist_ == NULL);
150
151 while (freelist_ != NULL) {
152 Event* event = freelist_;
153 freelist_ = event->next_;
154 delete event;
155 }
156}
157
158
159ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
160 LockGuard<Mutex> lock_guard(&mutex_);
161
162 // Grab an event from the free list or create a new one.
163 Event* event = freelist_;
164 if (event != NULL) {
165 freelist_ = event->next_;
166 } else {
167 event = new Event;
168 }
169 event->thread_ = GetCurrentThread();
170 event->notified_ = false;
171
172#ifdef DEBUG
173 // The event must not be on the wait list.
174 for (Event* we = waitlist_; we != NULL; we = we->next_) {
175 DCHECK_NE(event, we);
176 }
177#endif
178
179 // Prepend the event to the wait list.
180 event->next_ = waitlist_;
181 waitlist_ = event;
182
183 return event;
184}
185
186
187void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
188 LockGuard<Mutex> lock_guard(&mutex_);
189
190 // Remove the event from the wait list.
191 for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000192 DCHECK(*wep);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000193 if (*wep == event) {
194 *wep = event->next_;
195 break;
196 }
197 }
198
199#ifdef DEBUG
200 // The event must not be on the free list.
201 for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
202 DCHECK_NE(event, fe);
203 }
204#endif
205
206 // Reset the event.
207 BOOL ok = ::ResetEvent(event->handle_);
208 DCHECK(ok);
209 USE(ok);
210
211 // Insert the event into the free list.
212 event->next_ = freelist_;
213 freelist_ = event;
214
215 // Forward signals delivered after the timeout to the next waiting event.
216 if (!result && event->notified_ && waitlist_ != NULL) {
217 ok = ::SetEvent(waitlist_->handle_);
218 DCHECK(ok);
219 USE(ok);
220 waitlist_->notified_ = true;
221 }
222}
223
224
225ConditionVariable::ConditionVariable() {}
226
227
228ConditionVariable::~ConditionVariable() {}
229
230
231void ConditionVariable::NotifyOne() {
232 // Notify the thread with the highest priority in the waitlist
233 // that was not already signalled.
234 LockGuard<Mutex> lock_guard(native_handle_.mutex());
235 Event* highest_event = NULL;
236 int highest_priority = std::numeric_limits<int>::min();
237 for (Event* event = native_handle().waitlist();
238 event != NULL;
239 event = event->next_) {
240 if (event->notified_) {
241 continue;
242 }
243 int priority = GetThreadPriority(event->thread_);
244 DCHECK_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
245 if (priority >= highest_priority) {
246 highest_priority = priority;
247 highest_event = event;
248 }
249 }
250 if (highest_event != NULL) {
251 DCHECK(!highest_event->notified_);
252 ::SetEvent(highest_event->handle_);
253 highest_event->notified_ = true;
254 }
255}
256
257
258void ConditionVariable::NotifyAll() {
259 // Notify all threads on the waitlist.
260 LockGuard<Mutex> lock_guard(native_handle_.mutex());
261 for (Event* event = native_handle().waitlist();
262 event != NULL;
263 event = event->next_) {
264 if (!event->notified_) {
265 ::SetEvent(event->handle_);
266 event->notified_ = true;
267 }
268 }
269}
270
271
272void ConditionVariable::Wait(Mutex* mutex) {
273 // Create and setup the wait event.
274 Event* event = native_handle_.Pre();
275
276 // Release the user mutex.
277 mutex->Unlock();
278
279 // Wait on the wait event.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000280 while (!event->WaitFor(INFINITE)) {
281 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000282
283 // Reaquire the user mutex.
284 mutex->Lock();
285
286 // Release the wait event (we must have been notified).
287 DCHECK(event->notified_);
288 native_handle_.Post(event, true);
289}
290
291
292bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
293 // Create and setup the wait event.
294 Event* event = native_handle_.Pre();
295
296 // Release the user mutex.
297 mutex->Unlock();
298
299 // Wait on the wait event.
300 TimeTicks now = TimeTicks::Now();
301 TimeTicks end = now + rel_time;
302 bool result = false;
303 while (true) {
304 int64_t msec = (end - now).InMilliseconds();
305 if (msec >= static_cast<int64_t>(INFINITE)) {
306 result = event->WaitFor(INFINITE - 1);
307 if (result) {
308 break;
309 }
310 now = TimeTicks::Now();
311 } else {
312 result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
313 break;
314 }
315 }
316
317 // Reaquire the user mutex.
318 mutex->Lock();
319
320 // Release the wait event.
321 DCHECK(!result || event->notified_);
322 native_handle_.Post(event, result);
323
324 return result;
325}
326
327#endif // V8_OS_POSIX
328
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000329} // namespace base
330} // namespace v8