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 26namespace v8 { 27 28namespace base { 29class TimeDelta; 30} // base 31 32namespace internal { 33 34class Isolate; 35class JSArrayBuffer; 36 37class 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 65class 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 82class 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