1 // 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.
5 #include "src/base/platform/condition-variable.h"
10 #include "src/base/platform/time.h"
17 ConditionVariable::ConditionVariable() {
18 // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
19 // hack to support cross-compiling Chrome for Android in AOSP. Remove
20 // this once AOSP is fixed.
21 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
22 (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
23 // On Free/Net/OpenBSD and Linux with glibc we can change the time
24 // source for pthread_cond_timedwait() to use the monotonic clock.
25 pthread_condattr_t attr;
26 int result = pthread_condattr_init(&attr);
28 result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
30 result = pthread_cond_init(&native_handle_, &attr);
32 result = pthread_condattr_destroy(&attr);
34 int result = pthread_cond_init(&native_handle_, NULL);
41 ConditionVariable::~ConditionVariable() {
42 int result = pthread_cond_destroy(&native_handle_);
48 void ConditionVariable::NotifyOne() {
49 int result = pthread_cond_signal(&native_handle_);
55 void ConditionVariable::NotifyAll() {
56 int result = pthread_cond_broadcast(&native_handle_);
62 void ConditionVariable::Wait(Mutex* mutex) {
63 mutex->AssertHeldAndUnmark();
64 int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
67 mutex->AssertUnheldAndMark();
71 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
74 mutex->AssertHeldAndUnmark();
76 // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
77 // not depend on the real time clock, which is what you really WANT here!
78 ts = rel_time.ToTimespec();
79 DCHECK_GE(ts.tv_sec, 0);
80 DCHECK_GE(ts.tv_nsec, 0);
81 result = pthread_cond_timedwait_relative_np(
82 &native_handle_, &mutex->native_handle(), &ts);
84 // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
85 // hack to support cross-compiling Chrome for Android in AOSP. Remove
86 // this once AOSP is fixed.
87 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
88 (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
89 // On Free/Net/OpenBSD and Linux with glibc we can change the time
90 // source for pthread_cond_timedwait() to use the monotonic clock.
91 result = clock_gettime(CLOCK_MONOTONIC, &ts);
93 Time now = Time::FromTimespec(ts);
95 // The timeout argument to pthread_cond_timedwait() is in absolute time.
96 Time now = Time::NowFromSystemTime();
98 Time end_time = now + rel_time;
99 DCHECK_GE(end_time, now);
100 ts = end_time.ToTimespec();
101 result = pthread_cond_timedwait(
102 &native_handle_, &mutex->native_handle(), &ts);
103 #endif // V8_OS_MACOSX
104 mutex->AssertUnheldAndMark();
105 if (result == ETIMEDOUT) {
108 DCHECK_EQ(0, result);
114 struct ConditionVariable::Event {
115 Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
116 DCHECK(handle_ != NULL);
120 BOOL ok = ::CloseHandle(handle_);
125 bool WaitFor(DWORD timeout_ms) {
126 DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
127 if (result == WAIT_OBJECT_0) {
130 DCHECK(result == WAIT_TIMEOUT);
137 volatile bool notified_;
141 ConditionVariable::NativeHandle::~NativeHandle() {
142 DCHECK(waitlist_ == NULL);
144 while (freelist_ != NULL) {
145 Event* event = freelist_;
146 freelist_ = event->next_;
152 ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
153 LockGuard<Mutex> lock_guard(&mutex_);
155 // Grab an event from the free list or create a new one.
156 Event* event = freelist_;
158 freelist_ = event->next_;
162 event->thread_ = GetCurrentThread();
163 event->notified_ = false;
166 // The event must not be on the wait list.
167 for (Event* we = waitlist_; we != NULL; we = we->next_) {
168 DCHECK_NE(event, we);
172 // Prepend the event to the wait list.
173 event->next_ = waitlist_;
180 void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
181 LockGuard<Mutex> lock_guard(&mutex_);
183 // Remove the event from the wait list.
184 for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
193 // The event must not be on the free list.
194 for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
195 DCHECK_NE(event, fe);
200 BOOL ok = ::ResetEvent(event->handle_);
204 // Insert the event into the free list.
205 event->next_ = freelist_;
208 // Forward signals delivered after the timeout to the next waiting event.
209 if (!result && event->notified_ && waitlist_ != NULL) {
210 ok = ::SetEvent(waitlist_->handle_);
213 waitlist_->notified_ = true;
218 ConditionVariable::ConditionVariable() {}
221 ConditionVariable::~ConditionVariable() {}
224 void ConditionVariable::NotifyOne() {
225 // Notify the thread with the highest priority in the waitlist
226 // that was not already signalled.
227 LockGuard<Mutex> lock_guard(native_handle_.mutex());
228 Event* highest_event = NULL;
229 int highest_priority = std::numeric_limits<int>::min();
230 for (Event* event = native_handle().waitlist();
232 event = event->next_) {
233 if (event->notified_) {
236 int priority = GetThreadPriority(event->thread_);
237 DCHECK_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
238 if (priority >= highest_priority) {
239 highest_priority = priority;
240 highest_event = event;
243 if (highest_event != NULL) {
244 DCHECK(!highest_event->notified_);
245 ::SetEvent(highest_event->handle_);
246 highest_event->notified_ = true;
251 void ConditionVariable::NotifyAll() {
252 // Notify all threads on the waitlist.
253 LockGuard<Mutex> lock_guard(native_handle_.mutex());
254 for (Event* event = native_handle().waitlist();
256 event = event->next_) {
257 if (!event->notified_) {
258 ::SetEvent(event->handle_);
259 event->notified_ = true;
265 void ConditionVariable::Wait(Mutex* mutex) {
266 // Create and setup the wait event.
267 Event* event = native_handle_.Pre();
269 // Release the user mutex.
272 // Wait on the wait event.
273 while (!event->WaitFor(INFINITE))
276 // Reaquire the user mutex.
279 // Release the wait event (we must have been notified).
280 DCHECK(event->notified_);
281 native_handle_.Post(event, true);
285 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
286 // Create and setup the wait event.
287 Event* event = native_handle_.Pre();
289 // Release the user mutex.
292 // Wait on the wait event.
293 TimeTicks now = TimeTicks::Now();
294 TimeTicks end = now + rel_time;
297 int64_t msec = (end - now).InMilliseconds();
298 if (msec >= static_cast<int64_t>(INFINITE)) {
299 result = event->WaitFor(INFINITE - 1);
303 now = TimeTicks::Now();
305 result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
310 // Reaquire the user mutex.
313 // Release the wait event.
314 DCHECK(!result || event->notified_);
315 native_handle_.Post(event, result);
320 #endif // V8_OS_POSIX
322 } } // namespace v8::base