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