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   // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
     19   // hack to support cross-compiling Chrome for Android in AOSP. Remove
     20   // this once AOSP is fixed.
     21 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
     22      (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
     23   // On Free/Net/OpenBSD and Linux with glibc we can change the time
     24   // source for pthread_cond_timedwait() to use the monotonic clock.
     25   pthread_condattr_t attr;
     26   int result = pthread_condattr_init(&attr);
     27   DCHECK_EQ(0, result);
     28   result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
     29   DCHECK_EQ(0, result);
     30   result = pthread_cond_init(&native_handle_, &attr);
     31   DCHECK_EQ(0, result);
     32   result = pthread_condattr_destroy(&attr);
     33 #else
     34   int result = pthread_cond_init(&native_handle_, NULL);
     35 #endif
     36   DCHECK_EQ(0, result);
     37   USE(result);
     38 }
     39 
     40 
     41 ConditionVariable::~ConditionVariable() {
     42   int result = pthread_cond_destroy(&native_handle_);
     43   DCHECK_EQ(0, result);
     44   USE(result);
     45 }
     46 
     47 
     48 void ConditionVariable::NotifyOne() {
     49   int result = pthread_cond_signal(&native_handle_);
     50   DCHECK_EQ(0, result);
     51   USE(result);
     52 }
     53 
     54 
     55 void ConditionVariable::NotifyAll() {
     56   int result = pthread_cond_broadcast(&native_handle_);
     57   DCHECK_EQ(0, result);
     58   USE(result);
     59 }
     60 
     61 
     62 void ConditionVariable::Wait(Mutex* mutex) {
     63   mutex->AssertHeldAndUnmark();
     64   int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
     65   DCHECK_EQ(0, result);
     66   USE(result);
     67   mutex->AssertUnheldAndMark();
     68 }
     69 
     70 
     71 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
     72   struct timespec ts;
     73   int result;
     74   mutex->AssertHeldAndUnmark();
     75 #if V8_OS_MACOSX
     76   // Mac OS X provides pthread_cond_timedwait_relative_np(), which does
     77   // not depend on the real time clock, which is what you really WANT here!
     78   ts = rel_time.ToTimespec();
     79   DCHECK_GE(ts.tv_sec, 0);
     80   DCHECK_GE(ts.tv_nsec, 0);
     81   result = pthread_cond_timedwait_relative_np(
     82       &native_handle_, &mutex->native_handle(), &ts);
     83 #else
     84   // TODO(bmeurer): The test for V8_LIBRT_NOT_AVAILABLE is a temporary
     85   // hack to support cross-compiling Chrome for Android in AOSP. Remove
     86   // this once AOSP is fixed.
     87 #if (V8_OS_FREEBSD || V8_OS_NETBSD || V8_OS_OPENBSD || \
     88      (V8_OS_LINUX && V8_LIBC_GLIBC)) && !V8_LIBRT_NOT_AVAILABLE
     89   // On Free/Net/OpenBSD and Linux with glibc we can change the time
     90   // source for pthread_cond_timedwait() to use the monotonic clock.
     91   result = clock_gettime(CLOCK_MONOTONIC, &ts);
     92   DCHECK_EQ(0, result);
     93   Time now = Time::FromTimespec(ts);
     94 #else
     95   // The timeout argument to pthread_cond_timedwait() is in absolute time.
     96   Time now = Time::NowFromSystemTime();
     97 #endif
     98   Time end_time = now + rel_time;
     99   DCHECK_GE(end_time, now);
    100   ts = end_time.ToTimespec();
    101   result = pthread_cond_timedwait(
    102       &native_handle_, &mutex->native_handle(), &ts);
    103 #endif  // V8_OS_MACOSX
    104   mutex->AssertUnheldAndMark();
    105   if (result == ETIMEDOUT) {
    106     return false;
    107   }
    108   DCHECK_EQ(0, result);
    109   return true;
    110 }
    111 
    112 #elif V8_OS_WIN
    113 
    114 struct ConditionVariable::Event {
    115   Event() : handle_(::CreateEventA(NULL, true, false, NULL)) {
    116     DCHECK(handle_ != NULL);
    117   }
    118 
    119   ~Event() {
    120     BOOL ok = ::CloseHandle(handle_);
    121     DCHECK(ok);
    122     USE(ok);
    123   }
    124 
    125   bool WaitFor(DWORD timeout_ms) {
    126     DWORD result = ::WaitForSingleObject(handle_, timeout_ms);
    127     if (result == WAIT_OBJECT_0) {
    128       return true;
    129     }
    130     DCHECK(result == WAIT_TIMEOUT);
    131     return false;
    132   }
    133 
    134   HANDLE handle_;
    135   Event* next_;
    136   HANDLE thread_;
    137   volatile bool notified_;
    138 };
    139 
    140 
    141 ConditionVariable::NativeHandle::~NativeHandle() {
    142   DCHECK(waitlist_ == NULL);
    143 
    144   while (freelist_ != NULL) {
    145     Event* event = freelist_;
    146     freelist_ = event->next_;
    147     delete event;
    148   }
    149 }
    150 
    151 
    152 ConditionVariable::Event* ConditionVariable::NativeHandle::Pre() {
    153   LockGuard<Mutex> lock_guard(&mutex_);
    154 
    155   // Grab an event from the free list or create a new one.
    156   Event* event = freelist_;
    157   if (event != NULL) {
    158     freelist_ = event->next_;
    159   } else {
    160     event = new Event;
    161   }
    162   event->thread_ = GetCurrentThread();
    163   event->notified_ = false;
    164 
    165 #ifdef DEBUG
    166   // The event must not be on the wait list.
    167   for (Event* we = waitlist_; we != NULL; we = we->next_) {
    168     DCHECK_NE(event, we);
    169   }
    170 #endif
    171 
    172   // Prepend the event to the wait list.
    173   event->next_ = waitlist_;
    174   waitlist_ = event;
    175 
    176   return event;
    177 }
    178 
    179 
    180 void ConditionVariable::NativeHandle::Post(Event* event, bool result) {
    181   LockGuard<Mutex> lock_guard(&mutex_);
    182 
    183   // Remove the event from the wait list.
    184   for (Event** wep = &waitlist_;; wep = &(*wep)->next_) {
    185     DCHECK_NE(NULL, *wep);
    186     if (*wep == event) {
    187       *wep = event->next_;
    188       break;
    189     }
    190   }
    191 
    192 #ifdef DEBUG
    193   // The event must not be on the free list.
    194   for (Event* fe = freelist_; fe != NULL; fe = fe->next_) {
    195     DCHECK_NE(event, fe);
    196   }
    197 #endif
    198 
    199   // Reset the event.
    200   BOOL ok = ::ResetEvent(event->handle_);
    201   DCHECK(ok);
    202   USE(ok);
    203 
    204   // Insert the event into the free list.
    205   event->next_ = freelist_;
    206   freelist_ = event;
    207 
    208   // Forward signals delivered after the timeout to the next waiting event.
    209   if (!result && event->notified_ && waitlist_ != NULL) {
    210     ok = ::SetEvent(waitlist_->handle_);
    211     DCHECK(ok);
    212     USE(ok);
    213     waitlist_->notified_ = true;
    214   }
    215 }
    216 
    217 
    218 ConditionVariable::ConditionVariable() {}
    219 
    220 
    221 ConditionVariable::~ConditionVariable() {}
    222 
    223 
    224 void ConditionVariable::NotifyOne() {
    225   // Notify the thread with the highest priority in the waitlist
    226   // that was not already signalled.
    227   LockGuard<Mutex> lock_guard(native_handle_.mutex());
    228   Event* highest_event = NULL;
    229   int highest_priority = std::numeric_limits<int>::min();
    230   for (Event* event = native_handle().waitlist();
    231        event != NULL;
    232        event = event->next_) {
    233     if (event->notified_) {
    234       continue;
    235     }
    236     int priority = GetThreadPriority(event->thread_);
    237     DCHECK_NE(THREAD_PRIORITY_ERROR_RETURN, priority);
    238     if (priority >= highest_priority) {
    239       highest_priority = priority;
    240       highest_event = event;
    241     }
    242   }
    243   if (highest_event != NULL) {
    244     DCHECK(!highest_event->notified_);
    245     ::SetEvent(highest_event->handle_);
    246     highest_event->notified_ = true;
    247   }
    248 }
    249 
    250 
    251 void ConditionVariable::NotifyAll() {
    252   // Notify all threads on the waitlist.
    253   LockGuard<Mutex> lock_guard(native_handle_.mutex());
    254   for (Event* event = native_handle().waitlist();
    255        event != NULL;
    256        event = event->next_) {
    257     if (!event->notified_) {
    258       ::SetEvent(event->handle_);
    259       event->notified_ = true;
    260     }
    261   }
    262 }
    263 
    264 
    265 void ConditionVariable::Wait(Mutex* mutex) {
    266   // Create and setup the wait event.
    267   Event* event = native_handle_.Pre();
    268 
    269   // Release the user mutex.
    270   mutex->Unlock();
    271 
    272   // Wait on the wait event.
    273   while (!event->WaitFor(INFINITE))
    274     ;
    275 
    276   // Reaquire the user mutex.
    277   mutex->Lock();
    278 
    279   // Release the wait event (we must have been notified).
    280   DCHECK(event->notified_);
    281   native_handle_.Post(event, true);
    282 }
    283 
    284 
    285 bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
    286   // Create and setup the wait event.
    287   Event* event = native_handle_.Pre();
    288 
    289   // Release the user mutex.
    290   mutex->Unlock();
    291 
    292   // Wait on the wait event.
    293   TimeTicks now = TimeTicks::Now();
    294   TimeTicks end = now + rel_time;
    295   bool result = false;
    296   while (true) {
    297     int64_t msec = (end - now).InMilliseconds();
    298     if (msec >= static_cast<int64_t>(INFINITE)) {
    299       result = event->WaitFor(INFINITE - 1);
    300       if (result) {
    301         break;
    302       }
    303       now = TimeTicks::Now();
    304     } else {
    305       result = event->WaitFor((msec < 0) ? 0 : static_cast<DWORD>(msec));
    306       break;
    307     }
    308   }
    309 
    310   // Reaquire the user mutex.
    311   mutex->Lock();
    312 
    313   // Release the wait event.
    314   DCHECK(!result || event->notified_);
    315   native_handle_.Post(event, result);
    316 
    317   return result;
    318 }
    319 
    320 #endif  // V8_OS_POSIX
    321 
    322 } }  // namespace v8::base
    323