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   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