LCOV - code coverage report
Current view: top level - src/base/platform - condition-variable.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 25 26 96.2 %
Date: 2017-04-26 Functions: 6 6 100.0 %

          Line data    Source code
       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      303962 : 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      303962 :   int result = pthread_condattr_init(&attr);
      24             :   DCHECK_EQ(0, result);
      25      303962 :   result = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
      26             :   DCHECK_EQ(0, result);
      27      303962 :   result = pthread_cond_init(&native_handle_, &attr);
      28             :   DCHECK_EQ(0, result);
      29      303962 :   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      303962 : }
      36             : 
      37             : 
      38      296473 : 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      296473 :   int result = pthread_cond_destroy(&native_handle_);
      53             :   DCHECK_EQ(0, result);
      54             :   USE(result);
      55      296474 : }
      56             : 
      57             : 
      58      437248 : void ConditionVariable::NotifyOne() {
      59      437248 :   int result = pthread_cond_signal(&native_handle_);
      60             :   DCHECK_EQ(0, result);
      61             :   USE(result);
      62      437248 : }
      63             : 
      64             : 
      65        1595 : void ConditionVariable::NotifyAll() {
      66        1595 :   int result = pthread_cond_broadcast(&native_handle_);
      67             :   DCHECK_EQ(0, result);
      68             :   USE(result);
      69        1595 : }
      70             : 
      71             : 
      72        6947 : void ConditionVariable::Wait(Mutex* mutex) {
      73             :   mutex->AssertHeldAndUnmark();
      74        6947 :   int result = pthread_cond_wait(&native_handle_, &mutex->native_handle());
      75             :   DCHECK_EQ(0, result);
      76             :   USE(result);
      77             :   mutex->AssertUnheldAndMark();
      78        6947 : }
      79             : 
      80             : 
      81          34 : 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          34 :   result = clock_gettime(CLOCK_MONOTONIC, &ts);
      99             :   DCHECK_EQ(0, result);
     100          34 :   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          34 :   Time end_time = now + rel_time;
     106             :   DCHECK_GE(end_time, now);
     107          34 :   ts = end_time.ToTimespec();
     108             :   result = pthread_cond_timedwait(
     109          34 :       &native_handle_, &mutex->native_handle(), &ts);
     110             : #endif  // V8_OS_MACOSX
     111             :   mutex->AssertUnheldAndMark();
     112          34 :   if (result == ETIMEDOUT) {
     113             :     return false;
     114             :   }
     115             :   DCHECK_EQ(0, result);
     116           0 :   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

Generated by: LCOV version 1.10