1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "platform/condition-variable.h"
29
30 #include <cerrno>
31 #include <ctime>
32
33 #include "platform/time.h"
34
35 namespace v8 {
36 namespace internal {
37
38 #if V8_OS_POSIX
39
ConditionVariable()40 ConditionVariable::ConditionVariable() {
41 // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
42 // hack to support cross-compiling Chrome for Android in AOSP. Remove
43 // this once AOSP is fixed.
44 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
45 (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
46 // On Free/Net/OpenBSD and Linux with glibc we can change the time
47 // source for pthread_cond_timedwait() to use the monotonic clock.
48 pthread_condattr_t attr;
49 int result = pthread_condattr_init(&attr);
50 ASSERT_EQ(0, result);
51 result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
52 ASSERT_EQ(0, result);
53 result = pthread_cond_init(&native_handle_, &attr);
54 ASSERT_EQ(0, result);
55 result = pthread_condattr_destroy(&attr);
56 #else
57 int result = pthread_cond_init(&native_handle_, NULL);
58 #endif
59 ASSERT_EQ(0, result);
60 USE(result);
61 }
62
63
~ConditionVariable()64 ConditionVariable::~ConditionVariable() {
65 int result = pthread_cond_destroy(&native_handle_);
66 ASSERT_EQ(0, result);
67 USE(result);
68 }
69
70
NotifyOne()71 void ConditionVariable::NotifyOne() {
72 int result = pthread_cond_signal(&native_handle_);
73 ASSERT_EQ(0, result);
74 USE(result);
75 }
76
77
NotifyAll()78 void ConditionVariable::NotifyAll() {
79 int result = pthread_cond_broadcast(&native_handle_);
80 ASSERT_EQ(0, result);
81 USE(result);
82 }
83
84
Wait(Mutex * mutex)85 void ConditionVariable::Wait(Mutex* mutex) {
86 mutex->AssertHeldAndUnmark();
87 int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
88 ASSERT_EQ(0, result);
89 USE(result);
90 mutex->AssertUnheldAndMark();
91 }
92
93
WaitFor(Mutex * mutex,const TimeDelta & rel_time)94 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
95 struct timespec ts;
96 int result;
97 mutex->AssertHeldAndUnmark();
98 #if V8_OS_MACOSX
99 // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
100 // not depend on the real time clock, which is what you really WANT here!
101 ts = rel_time.ToTimespec();
102 ASSERT_GE(ts.tv_sec, 0);
103 ASSERT_GE(ts.tv_nsec, 0);
104 result = pthread_cond_timedwait_relative_np(
105 &native_handle_, &mutex->native_handle(), &ts);
106 #else
107 // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
108 // hack to support cross-compiling Chrome for Android in AOSP. Remove
109 // this once AOSP is fixed.
110 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
111 (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
112 // On Free/Net/OpenBSD and Linux with glibc we can change the time
113 // source for pthread_cond_timedwait() to use the monotonic clock.
114 result = clock_gettime(CLOCK_MONOTONIC, &ts);
115 ASSERT_EQ(0, result);
116 Time now = Time::FromTimespec(ts);
117 #else
118 // The timeout argument to pthread_cond_timedwait() is in absolute time.
119 Time now = Time::NowFromSystemTime();
120 #endif
121 Time end_time = now + rel_time;
122 ASSERT_GE(end_time, now);
123 ts = end_time.ToTimespec();
124 result = pthread_cond_timedwait(
125 &native_handle_, &mutex->native_handle(), &ts);
126 #endif // V8_OS_MACOSX
127 mutex->AssertUnheldAndMark();
128 if (result == ETIMEDOUT) {
129 return false;
130 }
131 ASSERT_EQ(0, result);
132 return true;
133 }
134
135 #elif V8_OS_WIN
136
137 struct ConditionVariable::Event {
138 Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
139 ASSERT(handle_ != NULL);
140 }
141
142 ~Event() {
143 BOOL ok = ::CloseHandle(handle_);
144 ASSERT(ok);
145 USE(ok);
146 }
147
148 bool WaitFor(DWORD timeout_ms) {
149 DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
150 if (result == WAIT_OBJECT_0) {
151 return true;
152 }
153 ASSERT(result == WAIT_TIMEOUT);
154 return false;
155 }
156
157 HANDLE handle_;
158 Event* next_;
159 HANDLE thread_;
160 volatile bool notified_;
161 };
162
163
164 ConditionVariable::NativeHandle::~NativeHandle() {
165 ASSERT(waitlist_ == NULL);
166
167 while (freelist_ != NULL) {
168 Event* event = freelist_;
169 freelist_ = event->next_;
170 delete event;
171 }
172 }
173
174
175 ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
176 LockGuard<Mutex> lock_guard(&mutex_);
177
178 // Grab an event from the free list or create a new one.
179 Event* event = freelist_;
180 if (event != NULL) {
181 freelist_ = event->next_;
182 } else {
183 event = new Event;
184 }
185 event->thread_ = GetCurrentThread();
186 event->notified_ = false;
187
188 #ifdef DEBUG
189 // The event must not be on the wait list.
190 for (Event* we = waitlist_; we != NULL; we = we->next_) {
191 ASSERT_NE(event, we);
192 }
193 #endif
194
195 // Prepend the event to the wait list.
196 event->next_ = waitlist_;
197 waitlist_ = event;
198
199 return event;
200 }
201
202
203 void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
204 LockGuard<Mutex> lock_guard(&mutex_);
205
206 // Remove the event from the wait list.
207 for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
208 ASSERT_NE(NULL, *wep);
209 if (*wep == event) {
210 *wep = event->next_;
211 break;
212 }
213 }
214
215 #ifdef DEBUG
216 // The event must not be on the free list.
217 for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
218 ASSERT_NE(event, fe);
219 }
220 #endif
221
222 // Reset the event.
223 BOOL ok = ::ResetEvent(event->handle_);
224 ASSERT(ok);
225 USE(ok);
226
227 // Insert the event into the free list.
228 event->next_ = freelist_;
229 freelist_ = event;
230
231 // Forward signals delivered after the timeout to the next waiting event.
232 if (!result && event->notified_ && waitlist_ != NULL) {
233 ok = ::SetEvent(waitlist_->handle_);
234 ASSERT(ok);
235 USE(ok);
236 waitlist_->notified_ = true;
237 }
238 }
239
240
241 ConditionVariable::ConditionVariable() {}
242
243
244 ConditionVariable::~ConditionVariable() {}
245
246
247 void ConditionVariable::NotifyOne() {
248 // Notify the thread with the highest priority in the waitlist
249 // that was not already signalled.
250 LockGuard<Mutex> lock_guard(native_handle_.mutex());
251 Event* highest_event = NULL;
252 int highest_priority = std::numeric_limits<int>::min();
253 for (Event* event = native_handle().waitlist();
254 event != NULL;
255 event = event->next_) {
256 if (event->notified_) {
257 continue;
258 }
259 int priority = GetThreadPriority(event->thread_);
260 ASSERT_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
261 if (priority >= highest_priority) {
262 highest_priority = priority;
263 highest_event = event;
264 }
265 }
266 if (highest_event != NULL) {
267 ASSERT(!highest_event->notified_);
268 ::SetEvent(highest_event->handle_);
269 highest_event->notified_ = true;
270 }
271 }
272
273
274 void ConditionVariable::NotifyAll() {
275 // Notify all threads on the waitlist.
276 LockGuard<Mutex> lock_guard(native_handle_.mutex());
277 for (Event* event = native_handle().waitlist();
278 event != NULL;
279 event = event->next_) {
280 if (!event->notified_) {
281 ::SetEvent(event->handle_);
282 event->notified_ = true;
283 }
284 }
285 }
286
287
288 void ConditionVariable::Wait(Mutex* mutex) {
289 // Create and setup the wait event.
290 Event* event = native_handle_.Pre();
291
292 // Release the user mutex.
293 mutex->Unlock();
294
295 // Wait on the wait event.
296 while (!event->WaitFor(INFINITE))
297 ;
298
299 // Reaquire the user mutex.
300 mutex->Lock();
301
302 // Release the wait event (we must have been notified).
303 ASSERT(event->notified_);
304 native_handle_.Post(event, true);
305 }
306
307
308 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
309 // Create and setup the wait event.
310 Event* event = native_handle_.Pre();
311
312 // Release the user mutex.
313 mutex->Unlock();
314
315 // Wait on the wait event.
316 TimeTicks now = TimeTicks::Now();
317 TimeTicks end = now + rel_time;
318 bool result = false;
319 while (true) {
320 int64_t msec = (end - now).InMilliseconds();
321 if (msec >= static_cast<int64_t>(INFINITE)) {
322 result = event->WaitFor(INFINITE - 1);
323 if (result) {
324 break;
325 }
326 now = TimeTicks::Now();
327 } else {
328 result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
329 break;
330 }
331 }
332
333 // Reaquire the user mutex.
334 mutex->Lock();
335
336 // Release the wait event.
337 ASSERT(!result || event->notified_);
338 native_handle_.Post(event, result);
339
340 return result;
341 }
342
343 #endif // V8_OS_POSIX
344
345 } } // namespace v8::internal
346