LCOV - code coverage report
Current view: top level - src - futex-emulation.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 76 80 95.0 %
Date: 2017-04-26 Functions: 4 7 57.1 %

          Line data    Source code
       1             : // Copyright 2015 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/futex-emulation.h"
       6             : 
       7             : #include <limits>
       8             : 
       9             : #include "src/base/macros.h"
      10             : #include "src/base/platform/time.h"
      11             : #include "src/conversions.h"
      12             : #include "src/handles-inl.h"
      13             : #include "src/isolate.h"
      14             : #include "src/list-inl.h"
      15             : #include "src/objects-inl.h"
      16             : 
      17             : namespace v8 {
      18             : namespace internal {
      19             : 
      20             : base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER;
      21             : base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
      22             :     LAZY_INSTANCE_INITIALIZER;
      23             : 
      24             : 
      25       16467 : void FutexWaitListNode::NotifyWake() {
      26             :   // Lock the FutexEmulation mutex before notifying. We know that the mutex
      27             :   // will have been unlocked if we are currently waiting on the condition
      28             :   // variable.
      29             :   //
      30             :   // The mutex may also not be locked if the other thread is currently handling
      31             :   // interrupts, or if FutexEmulation::Wait was just called and the mutex
      32             :   // hasn't been locked yet. In either of those cases, we set the interrupted
      33             :   // flag to true, which will be tested after the mutex is re-locked.
      34             :   base::LockGuard<base::Mutex> lock_guard(FutexEmulation::mutex_.Pointer());
      35       16467 :   if (waiting_) {
      36           6 :     cond_.NotifyOne();
      37           6 :     interrupted_ = true;
      38             :   }
      39       16467 : }
      40             : 
      41             : 
      42          12 : FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
      43             : 
      44             : 
      45           0 : void FutexWaitList::AddNode(FutexWaitListNode* node) {
      46             :   DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
      47         244 :   if (tail_) {
      48          42 :     tail_->next_ = node;
      49             :   } else {
      50         202 :     head_ = node;
      51             :   }
      52             : 
      53         244 :   node->prev_ = tail_;
      54         244 :   node->next_ = nullptr;
      55         244 :   tail_ = node;
      56           0 : }
      57             : 
      58             : 
      59           0 : void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
      60         244 :   if (node->prev_) {
      61           7 :     node->prev_->next_ = node->next_;
      62             :   } else {
      63         237 :     head_ = node->next_;
      64             :   }
      65             : 
      66         244 :   if (node->next_) {
      67          42 :     node->next_->prev_ = node->prev_;
      68             :   } else {
      69         202 :     tail_ = node->prev_;
      70             :   }
      71             : 
      72         244 :   node->prev_ = node->next_ = nullptr;
      73           0 : }
      74             : 
      75             : 
      76         272 : Object* FutexEmulation::Wait(Isolate* isolate,
      77             :                              Handle<JSArrayBuffer> array_buffer, size_t addr,
      78             :                              int32_t value, double rel_timeout_ms) {
      79             :   DCHECK(addr < NumberToSize(array_buffer->byte_length()));
      80             : 
      81             :   void* backing_store = array_buffer->backing_store();
      82             :   int32_t* p =
      83         272 :       reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
      84             : 
      85             :   base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
      86             : 
      87         272 :   if (*p != value) {
      88          28 :     return isolate->heap()->not_equal();
      89             :   }
      90             : 
      91         244 :   FutexWaitListNode* node = isolate->futex_wait_list_node();
      92             : 
      93         244 :   node->backing_store_ = backing_store;
      94         244 :   node->wait_addr_ = addr;
      95         244 :   node->waiting_ = true;
      96             : 
      97         244 :   bool use_timeout = rel_timeout_ms != V8_INFINITY;
      98             : 
      99             :   base::TimeDelta rel_timeout;
     100         244 :   if (use_timeout) {
     101             :     // Convert to nanoseconds.
     102          56 :     double rel_timeout_ns = rel_timeout_ms *
     103             :                             base::Time::kNanosecondsPerMicrosecond *
     104          56 :                             base::Time::kMicrosecondsPerMillisecond;
     105          56 :     if (rel_timeout_ns >
     106             :         static_cast<double>(std::numeric_limits<int64_t>::max())) {
     107             :       // 2**63 nanoseconds is 292 years. Let's just treat anything greater as
     108             :       // infinite.
     109             :       use_timeout = false;
     110             :     } else {
     111         112 :       rel_timeout = base::TimeDelta::FromNanoseconds(
     112          56 :           static_cast<int64_t>(rel_timeout_ns));
     113             :     }
     114             :   }
     115             : 
     116         244 :   base::TimeTicks start_time = base::TimeTicks::Now();
     117             :   base::TimeTicks timeout_time = start_time + rel_timeout;
     118             :   base::TimeTicks current_time = start_time;
     119             : 
     120             :   wait_list_.Pointer()->AddNode(node);
     121             : 
     122             :   Object* result;
     123             : 
     124             :   while (true) {
     125         442 :     bool interrupted = node->interrupted_;
     126         442 :     node->interrupted_ = false;
     127             : 
     128             :     // Unlock the mutex here to prevent deadlock from lock ordering between
     129             :     // mutex_ and mutexes locked by HandleInterrupts.
     130         442 :     mutex_.Pointer()->Unlock();
     131             : 
     132             :     // Because the mutex is unlocked, we have to be careful about not dropping
     133             :     // an interrupt. The notification can happen in three different places:
     134             :     // 1) Before Wait is called: the notification will be dropped, but
     135             :     //    interrupted_ will be set to 1. This will be checked below.
     136             :     // 2) After interrupted has been checked here, but before mutex_ is
     137             :     //    acquired: interrupted is checked again below, with mutex_ locked.
     138             :     //    Because the wakeup signal also acquires mutex_, we know it will not
     139             :     //    be able to notify until mutex_ is released below, when waiting on the
     140             :     //    condition variable.
     141             :     // 3) After the mutex is released in the call to WaitFor(): this
     142             :     // notification will wake up the condition variable. node->waiting() will
     143             :     // be false, so we'll loop and then check interrupts.
     144         442 :     if (interrupted) {
     145           6 :       Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
     146           6 :       if (interrupt_object->IsException(isolate)) {
     147             :         result = interrupt_object;
     148           6 :         mutex_.Pointer()->Lock();
     149           6 :         break;
     150             :       }
     151             :     }
     152             : 
     153         436 :     mutex_.Pointer()->Lock();
     154             : 
     155         436 :     if (node->interrupted_) {
     156             :       // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
     157             :       continue;
     158             :     }
     159             : 
     160         436 :     if (!node->waiting_) {
     161         182 :       result = isolate->heap()->ok();
     162         182 :       break;
     163             :     }
     164             : 
     165             :     // No interrupts, now wait.
     166         254 :     if (use_timeout) {
     167          70 :       current_time = base::TimeTicks::Now();
     168          70 :       if (current_time >= timeout_time) {
     169          56 :         result = isolate->heap()->timed_out();
     170          56 :         break;
     171             :       }
     172             : 
     173          14 :       base::TimeDelta time_until_timeout = timeout_time - current_time;
     174             :       DCHECK(time_until_timeout.InMicroseconds() >= 0);
     175             :       bool wait_for_result =
     176          14 :           node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
     177             :       USE(wait_for_result);
     178             :     } else {
     179         184 :       node->cond_.Wait(mutex_.Pointer());
     180             :     }
     181             : 
     182             :     // Spurious wakeup, interrupt or timeout.
     183             :   }
     184             : 
     185             :   wait_list_.Pointer()->RemoveNode(node);
     186         244 :   node->waiting_ = false;
     187             : 
     188         244 :   return result;
     189             : }
     190             : 
     191         168 : Object* FutexEmulation::Wake(Isolate* isolate,
     192             :                              Handle<JSArrayBuffer> array_buffer, size_t addr,
     193             :                              uint32_t num_waiters_to_wake) {
     194             :   DCHECK(addr < NumberToSize(array_buffer->byte_length()));
     195             : 
     196             :   int waiters_woken = 0;
     197             :   void* backing_store = array_buffer->backing_store();
     198             : 
     199             :   base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
     200         168 :   FutexWaitListNode* node = wait_list_.Pointer()->head_;
     201         518 :   while (node && num_waiters_to_wake > 0) {
     202         182 :     if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
     203         182 :       node->waiting_ = false;
     204         182 :       node->cond_.NotifyOne();
     205         182 :       if (num_waiters_to_wake != kWakeAll) {
     206         182 :         --num_waiters_to_wake;
     207             :       }
     208         182 :       waiters_woken++;
     209             :     }
     210             : 
     211         182 :     node = node->next_;
     212             :   }
     213             : 
     214         168 :   return Smi::FromInt(waiters_woken);
     215             : }
     216             : 
     217             : 
     218    18691990 : Object* FutexEmulation::NumWaitersForTesting(Isolate* isolate,
     219             :                                              Handle<JSArrayBuffer> array_buffer,
     220             :                                              size_t addr) {
     221             :   DCHECK(addr < NumberToSize(array_buffer->byte_length()));
     222             :   void* backing_store = array_buffer->backing_store();
     223             : 
     224             :   base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
     225             : 
     226             :   int waiters = 0;
     227    18691990 :   FutexWaitListNode* node = wait_list_.Pointer()->head_;
     228    38732824 :   while (node) {
     229     1348844 :     if (backing_store == node->backing_store_ && addr == node->wait_addr_ &&
     230             :         node->waiting_) {
     231     1348844 :       waiters++;
     232             :     }
     233             : 
     234     1348844 :     node = node->next_;
     235             :   }
     236             : 
     237    18691990 :   return Smi::FromInt(waiters);
     238             : }
     239             : 
     240             : }  // namespace internal
     241             : }  // namespace v8

Generated by: LCOV version 1.10