Home | History | Annotate | Download | only in src
      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 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   if (waiting_) {
     36     cond_.NotifyOne();
     37     interrupted_ = true;
     38   }
     39 }
     40 
     41 
     42 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
     43 
     44 
     45 void FutexWaitList::AddNode(FutexWaitListNode* node) {
     46   DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
     47   if (tail_) {
     48     tail_->next_ = node;
     49   } else {
     50     head_ = node;
     51   }
     52 
     53   node->prev_ = tail_;
     54   node->next_ = nullptr;
     55   tail_ = node;
     56 }
     57 
     58 
     59 void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
     60   if (node->prev_) {
     61     node->prev_->next_ = node->next_;
     62   } else {
     63     head_ = node->next_;
     64   }
     65 
     66   if (node->next_) {
     67     node->next_->prev_ = node->prev_;
     68   } else {
     69     tail_ = node->prev_;
     70   }
     71 
     72   node->prev_ = node->next_ = nullptr;
     73 }
     74 
     75 
     76 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       reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
     84 
     85   base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
     86 
     87   if (*p != value) {
     88     return isolate->heap()->not_equal();
     89   }
     90 
     91   FutexWaitListNode* node = isolate->futex_wait_list_node();
     92 
     93   node->backing_store_ = backing_store;
     94   node->wait_addr_ = addr;
     95   node->waiting_ = true;
     96 
     97   bool use_timeout = rel_timeout_ms != V8_INFINITY;
     98 
     99   base::TimeDelta rel_timeout;
    100   if (use_timeout) {
    101     // Convert to nanoseconds.
    102     double rel_timeout_ns = rel_timeout_ms *
    103                             base::Time::kNanosecondsPerMicrosecond *
    104                             base::Time::kMicrosecondsPerMillisecond;
    105     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       rel_timeout = base::TimeDelta::FromNanoseconds(
    112           static_cast<int64_t>(rel_timeout_ns));
    113     }
    114   }
    115 
    116   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     bool interrupted = node->interrupted_;
    126     node->interrupted_ = false;
    127 
    128     // Unlock the mutex here to prevent deadlock from lock ordering between
    129     // mutex_ and mutexes locked by HandleInterrupts.
    130     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     if (interrupted) {
    145       Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
    146       if (interrupt_object->IsException(isolate)) {
    147         result = interrupt_object;
    148         mutex_.Pointer()->Lock();
    149         break;
    150       }
    151     }
    152 
    153     mutex_.Pointer()->Lock();
    154 
    155     if (node->interrupted_) {
    156       // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
    157       continue;
    158     }
    159 
    160     if (!node->waiting_) {
    161       result = isolate->heap()->ok();
    162       break;
    163     }
    164 
    165     // No interrupts, now wait.
    166     if (use_timeout) {
    167       current_time = base::TimeTicks::Now();
    168       if (current_time >= timeout_time) {
    169         result = isolate->heap()->timed_out();
    170         break;
    171       }
    172 
    173       base::TimeDelta time_until_timeout = timeout_time - current_time;
    174       DCHECK(time_until_timeout.InMicroseconds() >= 0);
    175       bool wait_for_result =
    176           node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
    177       USE(wait_for_result);
    178     } else {
    179       node->cond_.Wait(mutex_.Pointer());
    180     }
    181 
    182     // Spurious wakeup, interrupt or timeout.
    183   }
    184 
    185   wait_list_.Pointer()->RemoveNode(node);
    186   node->waiting_ = false;
    187 
    188   return result;
    189 }
    190 
    191 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   FutexWaitListNode* node = wait_list_.Pointer()->head_;
    201   while (node && num_waiters_to_wake > 0) {
    202     if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
    203       node->waiting_ = false;
    204       node->cond_.NotifyOne();
    205       if (num_waiters_to_wake != kWakeAll) {
    206         --num_waiters_to_wake;
    207       }
    208       waiters_woken++;
    209     }
    210 
    211     node = node->next_;
    212   }
    213 
    214   return Smi::FromInt(waiters_woken);
    215 }
    216 
    217 
    218 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   FutexWaitListNode* node = wait_list_.Pointer()->head_;
    228   while (node) {
    229     if (backing_store == node->backing_store_ && addr == node->wait_addr_ &&
    230         node->waiting_) {
    231       waiters++;
    232     }
    233 
    234     node = node->next_;
    235   }
    236 
    237   return Smi::FromInt(waiters);
    238 }
    239 
    240 }  // namespace internal
    241 }  // namespace v8
    242