Line data Source code
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 :
17 303962 : 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 303962 : int result = pthread_condattr_init(&attr);
24 : DCHECK_EQ(0, result);
25 303962 : result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
26 : DCHECK_EQ(0, result);
27 303962 : result = pthread_cond_init(&native_handle_, &attr);
28 : DCHECK_EQ(0, result);
29 303962 : 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 303962 : }
36 :
37 :
38 296473 : 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 296473 : int result = pthread_cond_destroy(&native_handle_);
53 : DCHECK_EQ(0, result);
54 : USE(result);
55 296474 : }
56 :
57 :
58 437248 : void ConditionVariable::NotifyOne() {
59 437248 : int result = pthread_cond_signal(&native_handle_);
60 : DCHECK_EQ(0, result);
61 : USE(result);
62 437248 : }
63 :
64 :
65 1595 : void ConditionVariable::NotifyAll() {
66 1595 : int result = pthread_cond_broadcast(&native_handle_);
67 : DCHECK_EQ(0, result);
68 : USE(result);
69 1595 : }
70 :
71 :
72 6947 : void ConditionVariable::Wait(Mutex* mutex) {
73 : mutex->AssertHeldAndUnmark();
74 6947 : int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
75 : DCHECK_EQ(0, result);
76 : USE(result);
77 : mutex->AssertUnheldAndMark();
78 6947 : }
79 :
80 :
81 34 : 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 34 : result = clock_gettime(CLOCK_MONOTONIC, &ts);
99 : DCHECK_EQ(0, result);
100 34 : 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 34 : Time end_time = now + rel_time;
106 : DCHECK_GE(end_time, now);
107 34 : ts = end_time.ToTimespec();
108 : result = pthread_cond_timedwait(
109 34 : &native_handle_, &mutex->native_handle(), &ts);
110 : #endif // V8_OS_MACOSX
111 : mutex->AssertUnheldAndMark();
112 34 : if (result == ETIMEDOUT) {
113 : return false;
114 : }
115 : DCHECK_EQ(0, result);
116 0 : 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
|