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

Generated by: LCOV version 1.10