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 #include "src/handles.h" 17 18 // Support for emulating futexes, a low-level synchronization primitive. They 19 // are natively supported by Linux, but must be emulated for other platforms. 20 // This library emulates them on all platforms using mutexes and condition 21 // variables for consistency. 22 // 23 // This is used by the Futex API defined in the SharedArrayBuffer draft spec, 24 // found here: https://github.com/lars-t-hansen/ecmascript_sharedmem 25 26 namespace v8 { 27 28 namespace base { 29 class TimeDelta; 30 } // base 31 32 namespace internal { 33 34 class Isolate; 35 class JSArrayBuffer; 36 37 class FutexWaitListNode { 38 public: 39 FutexWaitListNode() 40 : prev_(nullptr), 41 next_(nullptr), 42 backing_store_(nullptr), 43 wait_addr_(0), 44 waiting_(false), 45 interrupted_(false) {} 46 47 void NotifyWake(); 48 49 private: 50 friend class FutexEmulation; 51 friend class FutexWaitList; 52 53 base::ConditionVariable cond_; 54 FutexWaitListNode* prev_; 55 FutexWaitListNode* next_; 56 void* backing_store_; 57 size_t wait_addr_; 58 bool waiting_; 59 bool interrupted_; 60 61 DISALLOW_COPY_AND_ASSIGN(FutexWaitListNode); 62 }; 63 64 65 class FutexWaitList { 66 public: 67 FutexWaitList(); 68 69 void AddNode(FutexWaitListNode* node); 70 void RemoveNode(FutexWaitListNode* node); 71 72 private: 73 friend class FutexEmulation; 74 75 FutexWaitListNode* head_; 76 FutexWaitListNode* tail_; 77 78 DISALLOW_COPY_AND_ASSIGN(FutexWaitList); 79 }; 80 81 82 class FutexEmulation : public AllStatic { 83 public: 84 // These must match the values in src/harmony-atomics.js 85 enum Result { 86 kOk = 0, 87 kNotEqual = -1, 88 kTimedOut = -2, 89 }; 90 91 // Check that array_buffer[addr] == value, and return kNotEqual if not. If 92 // they are equal, block execution on |isolate|'s thread until woken via 93 // |Wake|, or when the time given in |rel_timeout_ms| elapses. Note that 94 // |rel_timeout_ms| can be Infinity. 95 // If woken, return kOk, otherwise return kTimedOut. The initial check and 96 // the decision to wait happen atomically. 97 static Object* Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer, 98 size_t addr, int32_t value, double rel_timeout_ms); 99 100 // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|. 101 // The rest of the waiters will continue to wait. The return value is the 102 // number of woken waiters. 103 static Object* Wake(Isolate* isolate, Handle<JSArrayBuffer> array_buffer, 104 size_t addr, int num_waiters_to_wake); 105 106 // Check that array_buffer[addr] == value, and return kNotEqual if not. If 107 // they are equal, wake |num_waiters_to_wake| threads that are waiting on the 108 // given |addr|. The rest of the waiters will continue to wait, but will now 109 // be waiting on |addr2| instead of |addr|. The return value is the number of 110 // woken waiters or kNotEqual as described above. 111 static Object* WakeOrRequeue(Isolate* isolate, 112 Handle<JSArrayBuffer> array_buffer, size_t addr, 113 int num_waiters_to_wake, int32_t value, 114 size_t addr2); 115 116 // Return the number of threads waiting on |addr|. Should only be used for 117 // testing. 118 static Object* NumWaitersForTesting(Isolate* isolate, 119 Handle<JSArrayBuffer> array_buffer, 120 size_t addr); 121 122 private: 123 friend class FutexWaitListNode; 124 125 static base::LazyMutex mutex_; 126 static base::LazyInstance<FutexWaitList>::type wait_list_; 127 }; 128 } // namespace internal 129 } // namespace v8 130 131 #endif // V8_FUTEX_EMULATION_H_ 132