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 12namespace v8 { 13namespace base { 14 15#if V8_OS_POSIX 16 17ConditionVariable::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 38ConditionVariable::~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 58void ConditionVariable::NotifyOne() { 59 int result = pthread_cond_signal(&native_handle_); 60 DCHECK_EQ(0, result); 61 USE(result); 62} 63 64 65void ConditionVariable::NotifyAll() { 66 int result = pthread_cond_broadcast(&native_handle_); 67 DCHECK_EQ(0, result); 68 USE(result); 69} 70 71 72void 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 81bool 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 121struct 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 148ConditionVariable::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 159ConditionVariable::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 187void 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 225ConditionVariable::ConditionVariable() {} 226 227 228ConditionVariable::~ConditionVariable() {} 229 230 231void 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 258void 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 272void 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 292bool 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