Home | History | Annotate | Download | only in platform
      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 #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 
     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
     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 
    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_) {
    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 
    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.
    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 
    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 
    329 }  // namespace base
    330 }  // namespace v8
    331