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.
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 
12 namespace v8 {
13 namespace base {
14 
15 #if V8_OS_POSIX
16 
ConditionVariable()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);
27   DCHECK_EQ(0, result);
28   result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
29   DCHECK_EQ(0, result);
30   result = pthread_cond_init(&native_handle_, &attr);
31   DCHECK_EQ(0, result);
32   result = pthread_condattr_destroy(&attr);
33 #else
34   int result = pthread_cond_init(&native_handle_, NULL);
35 #endif
36   DCHECK_EQ(0, result);
37   USE(result);
38 }
39 
40 
~ConditionVariable()41 ConditionVariable::~ConditionVariable() {
42   int result = pthread_cond_destroy(&native_handle_);
43   DCHECK_EQ(0, result);
44   USE(result);
45 }
46 
47 
NotifyOne()48 void ConditionVariable::NotifyOne() {
49   int result = pthread_cond_signal(&native_handle_);
50   DCHECK_EQ(0, result);
51   USE(result);
52 }
53 
54 
NotifyAll()55 void ConditionVariable::NotifyAll() {
56   int result = pthread_cond_broadcast(&native_handle_);
57   DCHECK_EQ(0, result);
58   USE(result);
59 }
60 
61 
Wait(Mutex * mutex)62 void ConditionVariable::Wait(Mutex* mutex) {
63   mutex->AssertHeldAndUnmark();
64   int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
65   DCHECK_EQ(0, result);
66   USE(result);
67   mutex->AssertUnheldAndMark();
68 }
69 
70 
WaitFor(Mutex * mutex,const TimeDelta & rel_time)71 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
72   struct timespec ts;
73   int result;
74   mutex->AssertHeldAndUnmark();
75 #if V8_OS_MACOSX
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);
83 #else
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);
92   DCHECK_EQ(0, result);
93   Time now = Time::FromTimespec(ts);
94 #else
95   // The timeout argument to pthread_cond_timedwait() is in absolute time.
96   Time now = Time::NowFromSystemTime();
97 #endif
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) {
106     return false;
107   }
108   DCHECK_EQ(0, result);
109   return true;
110 }
111 
112 #elif V8_OS_WIN
113 
114 struct ConditionVariable::Event {
115   Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
116     DCHECK(handle_ != NULL);
117   }
118 
119   ~Event() {
120     BOOL ok = ::CloseHandle(handle_);
121     DCHECK(ok);
122     USE(ok);
123   }
124 
125   bool WaitFor(DWORD timeout_ms) {
126     DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
127     if (result == WAIT_OBJECT_0) {
128       return true;
129     }
130     DCHECK(result == WAIT_TIMEOUT);
131     return false;
132   }
133 
134   HANDLE handle_;
135   Event* next_;
136   HANDLE thread_;
137   volatile bool notified_;
138 };
139 
140 
141 ConditionVariable::NativeHandle::~NativeHandle() {
142   DCHECK(waitlist_ == NULL);
143 
144   while (freelist_ != NULL) {
145     Event* event = freelist_;
146     freelist_ = event->next_;
147     delete event;
148   }
149 }
150 
151 
152 ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
153   LockGuard<Mutex> lock_guard(&mutex_);
154 
155   // Grab an event from the free list or create a new one.
156   Event* event = freelist_;
157   if (event != NULL) {
158     freelist_ = event->next_;
159   } else {
160     event = new Event;
161   }
162   event->thread_ = GetCurrentThread();
163   event->notified_ = false;
164 
165 #ifdef DEBUG
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);
169   }
170 #endif
171 
172   // Prepend the event to the wait list.
173   event->next_ = waitlist_;
174   waitlist_ = event;
175 
176   return event;
177 }
178 
179 
180 void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
181   LockGuard<Mutex> lock_guard(&mutex_);
182 
183   // Remove the event from the wait list.
184   for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
185     DCHECK_NE(NULL, *wep);
186     if (*wep == event) {
187       *wep = event->next_;
188       break;
189     }
190   }
191 
192 #ifdef DEBUG
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);
196   }
197 #endif
198 
199   // Reset the event.
200   BOOL ok = ::ResetEvent(event->handle_);
201   DCHECK(ok);
202   USE(ok);
203 
204   // Insert the event into the free list.
205   event->next_ = freelist_;
206   freelist_ = event;
207 
208   // Forward signals delivered after the timeout to the next waiting event.
209   if (!result && event->notified_ && waitlist_ != NULL) {
210     ok = ::SetEvent(waitlist_->handle_);
211     DCHECK(ok);
212     USE(ok);
213     waitlist_->notified_ = true;
214   }
215 }
216 
217 
218 ConditionVariable::ConditionVariable() {}
219 
220 
221 ConditionVariable::~ConditionVariable() {}
222 
223 
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();
231        event != NULL;
232        event = event->next_) {
233     if (event->notified_) {
234       continue;
235     }
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;
241     }
242   }
243   if (highest_event != NULL) {
244     DCHECK(!highest_event->notified_);
245     ::SetEvent(highest_event->handle_);
246     highest_event->notified_ = true;
247   }
248 }
249 
250 
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();
255        event != NULL;
256        event = event->next_) {
257     if (!event->notified_) {
258       ::SetEvent(event->handle_);
259       event->notified_ = true;
260     }
261   }
262 }
263 
264 
265 void ConditionVariable::Wait(Mutex* mutex) {
266   // Create and setup the wait event.
267   Event* event = native_handle_.Pre();
268 
269   // Release the user mutex.
270   mutex->Unlock();
271 
272   // Wait on the wait event.
273   while (!event->WaitFor(INFINITE))
274     ;
275 
276   // Reaquire the user mutex.
277   mutex->Lock();
278 
279   // Release the wait event (we must have been notified).
280   DCHECK(event->notified_);
281   native_handle_.Post(event, true);
282 }
283 
284 
285 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
286   // Create and setup the wait event.
287   Event* event = native_handle_.Pre();
288 
289   // Release the user mutex.
290   mutex->Unlock();
291 
292   // Wait on the wait event.
293   TimeTicks now = TimeTicks::Now();
294   TimeTicks end = now + rel_time;
295   bool result = false;
296   while (true) {
297     int64_t msec = (end - now).InMilliseconds();
298     if (msec >= static_cast<int64_t>(INFINITE)) {
299       result = event->WaitFor(INFINITE - 1);
300       if (result) {
301         break;
302       }
303       now = TimeTicks::Now();
304     } else {
305       result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
306       break;
307     }
308   }
309 
310   // Reaquire the user mutex.
311   mutex->Lock();
312 
313   // Release the wait event.
314   DCHECK(!result || event->notified_);
315   native_handle_.Post(event, result);
316 
317   return result;
318 }
319 
320 #endif  // V8_OS_POSIX
321 
322 } }  // namespace v8::base
323