Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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() { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 18 | #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \ |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 19 | (V8_OS_LINUX && V8_LIBC_GLIBC)) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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() { |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 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 |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 52 | int result = pthread_cond_destroy(&native_handle_); |
| 53 | DCHECK_EQ(0, result); |
| 54 | USE(result); |
| 55 | } |
| 56 | |
| 57 | |
| 58 | void ConditionVariable::NotifyOne() { |
| 59 | int result = pthread_cond_signal(&native_handle_); |
| 60 | DCHECK_EQ(0, result); |
| 61 | USE(result); |
| 62 | } |
| 63 | |
| 64 | |
| 65 | void ConditionVariable::NotifyAll() { |
| 66 | int result = pthread_cond_broadcast(&native_handle_); |
| 67 | DCHECK_EQ(0, result); |
| 68 | USE(result); |
| 69 | } |
| 70 | |
| 71 | |
| 72 | void 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 | |
| 81 | 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 |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 94 | #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \ |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 95 | (V8_OS_LINUX && V8_LIBC_GLIBC)) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 | |
| 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_) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 192 | DCHECK(*wep); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 280 | while (!event->WaitFor(INFINITE)) { |
| 281 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 329 | } // namespace base |
| 330 | } // namespace v8 |