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 #ifndef V8_FUTEX_EMULATION_H_
      6 #define V8_FUTEX_EMULATION_H_
      7 
      8 #include <stdint.h>
      9 
     10 #include "src/allocation.h"
     11 #include "src/base/atomicops.h"
     12 #include "src/base/lazy-instance.h"
     13 #include "src/base/macros.h"
     14 #include "src/base/platform/condition-variable.h"
     15 #include "src/base/platform/mutex.h"
     16 
     17 // Support for emulating futexes, a low-level synchronization primitive. They
     18 // are natively supported by Linux, but must be emulated for other platforms.
     19 // This library emulates them on all platforms using mutexes and condition
     20 // variables for consistency.
     21 //
     22 // This is used by the Futex API defined in the SharedArrayBuffer draft spec,
     23 // found here: https://github.com/tc39/ecmascript_sharedmem
     24 
     25 namespace v8 {
     26 
     27 namespace base {
     28 class TimeDelta;
     29 }  // base
     30 
     31 namespace internal {
     32 
     33 template <typename T>
     34 class Handle;
     35 class Isolate;
     36 class JSArrayBuffer;
     37 
     38 class AtomicsWaitWakeHandle {
     39  public:
     40   explicit AtomicsWaitWakeHandle(Isolate* isolate) : isolate_(isolate) {}
     41 
     42   void Wake();
     43   inline bool has_stopped() const { return stopped_; }
     44 
     45  private:
     46   Isolate* isolate_;
     47   bool stopped_ = false;
     48 };
     49 
     50 class FutexWaitListNode {
     51  public:
     52   FutexWaitListNode()
     53       : prev_(nullptr),
     54         next_(nullptr),
     55         backing_store_(nullptr),
     56         wait_addr_(0),
     57         waiting_(false),
     58         interrupted_(false) {}
     59 
     60   void NotifyWake();
     61 
     62  private:
     63   friend class FutexEmulation;
     64   friend class FutexWaitList;
     65   friend class ResetWaitingOnScopeExit;
     66 
     67   base::ConditionVariable cond_;
     68   // prev_ and next_ are protected by FutexEmulation::mutex_.
     69   FutexWaitListNode* prev_;
     70   FutexWaitListNode* next_;
     71   void* backing_store_;
     72   size_t wait_addr_;
     73   // waiting_ and interrupted_ are protected by FutexEmulation::mutex_
     74   // if this node is currently contained in FutexEmulation::wait_list_
     75   // or an AtomicsWaitWakeHandle has access to it.
     76   bool waiting_;
     77   bool interrupted_;
     78 
     79   DISALLOW_COPY_AND_ASSIGN(FutexWaitListNode);
     80 };
     81 
     82 
     83 class FutexWaitList {
     84  public:
     85   FutexWaitList();
     86 
     87   void AddNode(FutexWaitListNode* node);
     88   void RemoveNode(FutexWaitListNode* node);
     89 
     90  private:
     91   friend class FutexEmulation;
     92 
     93   FutexWaitListNode* head_;
     94   FutexWaitListNode* tail_;
     95 
     96   DISALLOW_COPY_AND_ASSIGN(FutexWaitList);
     97 };
     98 
     99 class ResetWaitingOnScopeExit {
    100  public:
    101   explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {}
    102   ~ResetWaitingOnScopeExit() { node_->waiting_ = false; }
    103 
    104  private:
    105   FutexWaitListNode* node_;
    106 
    107   DISALLOW_COPY_AND_ASSIGN(ResetWaitingOnScopeExit);
    108 };
    109 
    110 class FutexEmulation : public AllStatic {
    111  public:
    112   // Pass to Wake() to wake all waiters.
    113   static const uint32_t kWakeAll = UINT32_MAX;
    114 
    115   // Check that array_buffer[addr] == value, and return "not-equal" if not. If
    116   // they are equal, block execution on |isolate|'s thread until woken via
    117   // |Wake|, or when the time given in |rel_timeout_ms| elapses. Note that
    118   // |rel_timeout_ms| can be Infinity.
    119   // If woken, return "ok", otherwise return "timed-out". The initial check and
    120   // the decision to wait happen atomically.
    121   static Object* Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
    122                       size_t addr, int32_t value, double rel_timeout_ms);
    123 
    124   // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
    125   // |num_waiters_to_wake| can be kWakeAll, in which case all waiters are
    126   // woken. The rest of the waiters will continue to wait. The return value is
    127   // the number of woken waiters.
    128   static Object* Wake(Handle<JSArrayBuffer> array_buffer, size_t addr,
    129                       uint32_t num_waiters_to_wake);
    130 
    131   // Return the number of threads waiting on |addr|. Should only be used for
    132   // testing.
    133   static Object* NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
    134                                       size_t addr);
    135 
    136  private:
    137   friend class FutexWaitListNode;
    138   friend class AtomicsWaitWakeHandle;
    139 
    140   // `mutex_` protects the composition of `wait_list_` (i.e. no elements may be
    141   // added or removed without holding this mutex), as well as the `waiting_`
    142   // and `interrupted_` fields for each individual list node that is currently
    143   // part of the list. It must be the mutex used together with the `cond_`
    144   // condition variable of such nodes.
    145   static base::LazyMutex mutex_;
    146   static base::LazyInstance<FutexWaitList>::type wait_list_;
    147 };
    148 }  // namespace internal
    149 }  // namespace v8
    150 
    151 #endif  // V8_FUTEX_EMULATION_H_
    152