AtomicsObject.h (6999B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef builtin_AtomicsObject_h 8 #define builtin_AtomicsObject_h 9 10 #include "mozilla/Maybe.h" 11 #include "mozilla/TimeStamp.h" 12 13 #include "threading/ConditionVariable.h" 14 #include "threading/ProtectedData.h" // js::ThreadData 15 #include "vm/NativeObject.h" 16 #include "vm/PlainObject.h" 17 18 namespace js { 19 20 class SharedArrayRawBuffer; 21 22 class AtomicsObject : public NativeObject { 23 public: 24 static const JSClass class_; 25 }; 26 27 enum class FutexWaiterKind { Sync, Async, ListHead }; 28 29 class FutexWaiter; 30 class SyncFutexWaiter; 31 class AsyncFutexWaiter; 32 33 class FutexWaiterListNode { 34 private: 35 FutexWaiterListNode* next_ = nullptr; // Lower priority node in waiter list 36 FutexWaiterListNode* prev_ = nullptr; // Higher priority node in waiter list 37 38 protected: 39 explicit FutexWaiterListNode(FutexWaiterKind kind) : kind_(kind) {} 40 FutexWaiterKind kind_; 41 42 public: 43 FutexWaiter* toWaiter() { 44 MOZ_ASSERT(kind_ != FutexWaiterKind::ListHead); 45 return reinterpret_cast<FutexWaiter*>(this); 46 } 47 48 FutexWaiterKind kind() const { return kind_; } 49 50 FutexWaiterListNode* next() { return next_; } 51 void setNext(FutexWaiterListNode* next) { next_ = next; } 52 FutexWaiterListNode* prev() { return prev_; } 53 void setPrev(FutexWaiterListNode* prev) { prev_ = prev; } 54 }; 55 56 class FutexWaiterListHead : public FutexWaiterListNode { 57 public: 58 FutexWaiterListHead() : FutexWaiterListNode(FutexWaiterKind::ListHead) { 59 setNext(this); 60 setPrev(this); 61 } 62 ~FutexWaiterListHead(); 63 }; 64 65 class FutexThread { 66 friend class AutoLockFutexAPI; 67 68 public: 69 [[nodiscard]] static bool initialize(); 70 static void destroy(); 71 72 static void lock(); 73 static void unlock(); 74 75 FutexThread(); 76 [[nodiscard]] bool initInstance(); 77 void destroyInstance(); 78 79 // Parameters to notify(). 80 enum NotifyReason { 81 NotifyExplicit, // Being asked to wake up by another thread 82 NotifyForJSInterrupt // Interrupt requested 83 }; 84 85 // Result codes from wait() and atomics_wait_impl(). 86 enum class WaitResult { 87 Error, // Error has been reported, just propagate error signal 88 NotEqual, // Did not wait because the values differed 89 OK, // Waited and was woken 90 TimedOut // Waited and timed out 91 }; 92 93 // Block the calling thread and wait. 94 // 95 // The futex lock must be held around this call. 96 // 97 // The timeout is the number of milliseconds, with fractional 98 // times allowed; specify mozilla::Nothing() for an indefinite 99 // wait. 100 // 101 // wait() will not wake up spuriously. 102 [[nodiscard]] WaitResult wait( 103 JSContext* cx, js::UniqueLock<js::Mutex>& locked, 104 const mozilla::Maybe<mozilla::TimeDuration>& timeout); 105 106 // Notify the thread this is associated with. 107 // 108 // The futex lock must be held around this call. (The sleeping 109 // thread will not wake up until the caller of Atomics.notify() 110 // releases the lock.) 111 // 112 // If the thread is not waiting then this method does nothing. 113 // 114 // If the thread is waiting in a call to wait() and the 115 // reason is NotifyExplicit then the wait() call will return 116 // with Woken. 117 // 118 // If the thread is waiting in a call to wait() and the 119 // reason is NotifyForJSInterrupt then the wait() will return 120 // with WaitingNotifiedForInterrupt; in the latter case the caller 121 // of wait() must handle the interrupt. 122 void notify(NotifyReason reason); 123 124 bool isWaiting(); 125 126 // If canWait() returns false (the default) then wait() is disabled 127 // on the thread to which the FutexThread belongs. 128 bool canWait() { return canWait_; } 129 130 void setCanWait(bool flag) { canWait_ = flag; } 131 132 private: 133 enum FutexState { 134 Idle, // We are not waiting or woken 135 Waiting, // We are waiting, nothing has happened yet 136 WaitingNotifiedForInterrupt, // We are waiting, but have been interrupted, 137 // and have not yet started running the 138 // interrupt handler 139 WaitingInterrupted, // We are waiting, but have been interrupted 140 // and are running the interrupt handler 141 Woken // Woken by a script call to Atomics.notify 142 }; 143 144 // Condition variable that this runtime will wait on. 145 js::ConditionVariable* cond_; 146 147 // Current futex state for this runtime. When not in a wait this 148 // is Idle; when in a wait it is Waiting or the reason the futex 149 // is about to wake up. 150 FutexState state_; 151 152 // Shared futex lock for all runtimes. We can perhaps do better, 153 // but any lock will need to be per-domain (consider SharedWorker) 154 // or coarser. 155 static mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent> lock_; 156 157 // A flag that controls whether waiting is allowed. 158 ThreadData<bool> canWait_; 159 }; 160 161 // Go to sleep if the int32_t value at the given address equals `value`. 162 [[nodiscard]] FutexThread::WaitResult atomics_wait_impl( 163 JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int32_t value, 164 const mozilla::Maybe<mozilla::TimeDuration>& timeout); 165 166 // Go to sleep if the int64_t value at the given address equals `value`. 167 [[nodiscard]] FutexThread::WaitResult atomics_wait_impl( 168 JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int64_t value, 169 const mozilla::Maybe<mozilla::TimeDuration>& timeout); 170 171 // If the int32_t value at the given address equals `value`, return a result 172 // object containing a promise that will be resolved when that address is 173 // notified. 174 [[nodiscard]] PlainObject* atomics_wait_async_impl( 175 JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int32_t value, 176 const mozilla::Maybe<mozilla::TimeDuration>& timeout); 177 178 // If the int64_t value at the given address equals `value`, return a result 179 // object containing a promise that will be resolved when that address is 180 // notified. 181 [[nodiscard]] PlainObject* atomics_wait_async_impl( 182 JSContext* cx, SharedArrayRawBuffer* sarb, size_t byteOffset, int64_t value, 183 const mozilla::Maybe<mozilla::TimeDuration>& timeout); 184 185 // Notify some waiters on the given address. If `count` is negative then notify 186 // all. The return value is nonnegative and is the number of waiters woken. If 187 // the number of waiters woken exceeds INT64_MAX then this never returns. If 188 // `count` is nonnegative then the woken out parameter is never greater than 189 // `count`. 190 [[nodiscard]] bool atomics_notify_impl(JSContext* cx, 191 SharedArrayRawBuffer* sarb, 192 size_t byteOffset, int64_t count, 193 int64_t* woken); 194 195 } /* namespace js */ 196 197 #endif /* builtin_AtomicsObject_h */