• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
19      (V8_OS_LINUX && V8_LIBC_GLIBC))
20   // 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 
~ConditionVariable()38 ConditionVariable::~ConditionVariable() {
39 #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
52   int result = pthread_cond_destroy(&native_handle_);
53   DCHECK_EQ(0, result);
54   USE(result);
55 }
56 
57 
NotifyOne()58 void ConditionVariable::NotifyOne() {
59   int result = pthread_cond_signal(&native_handle_);
60   DCHECK_EQ(0, result);
61   USE(result);
62 }
63 
64 
NotifyAll()65 void ConditionVariable::NotifyAll() {
66   int result = pthread_cond_broadcast(&native_handle_);
67   DCHECK_EQ(0, result);
68   USE(result);
69 }
70 
71 
Wait(Mutex * mutex)72 void 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 
WaitFor(Mutex * mutex,const TimeDelta & rel_time)81 bool 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
94 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
95      (V8_OS_LINUX && V8_LIBC_GLIBC))
96   // 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 
121 struct 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 
148 ConditionVariable::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 
159 ConditionVariable::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 
187 void 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_) {
192     DCHECK(*wep);
193     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 
225 ConditionVariable::ConditionVariable() {}
226 
227 
228 ConditionVariable::~ConditionVariable() {}
229 
230 
231 void 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 
258 void 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 
272 void 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.
280   while (!event->WaitFor(INFINITE)) {
281   }
282 
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 
292 bool 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 
329 }  // namespace base
330 }  // namespace v8
331