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 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 38 ConditionVariable::~ConditionVariable() { 39 int result = pthread_cond_destroy(&native_handle_); 40 DCHECK_EQ(0, result); 41 USE(result); 42 } 43 44 45 void ConditionVariable::NotifyOne() { 46 int result = pthread_cond_signal(&native_handle_); 47 DCHECK_EQ(0, result); 48 USE(result); 49 } 50 51 52 void ConditionVariable::NotifyAll() { 53 int result = pthread_cond_broadcast(&native_handle_); 54 DCHECK_EQ(0, result); 55 USE(result); 56 } 57 58 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 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