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