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: AtomicsWaitWakeHandle(Isolate * isolate)40 explicit AtomicsWaitWakeHandle(Isolate* isolate) : isolate_(isolate) {} 41 42 void Wake(); has_stopped()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: FutexWaitListNode()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: ResetWaitingOnScopeExit(FutexWaitListNode * node)101 explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {} ~ResetWaitingOnScopeExit()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