v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
futex-emulation.h
Go to the documentation of this file.
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_EXECUTION_FUTEX_EMULATION_H_
6#define V8_EXECUTION_FUTEX_EMULATION_H_
7
8#include <stdint.h>
9
11#include "src/base/atomicops.h"
12#include "src/base/macros.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/tc39/ecmascript_sharedmem
25
26namespace v8 {
27
28class Promise;
29
30namespace base {
31class TimeDelta;
32} // namespace base
33
34namespace internal {
35
36class BackingStore;
37class FutexWaitList;
38
39class Isolate;
40class JSArrayBuffer;
41
43 public:
44 // Create a sync FutexWaitListNode.
45 FutexWaitListNode() = default;
46
47 // Create an async FutexWaitListNode.
48 FutexWaitListNode(std::weak_ptr<BackingStore> backing_store,
49 void* wait_location,
50 DirectHandle<JSObject> promise_capability,
51 Isolate* isolate);
52
53 // Disallow copying nodes.
56
57 void NotifyWake();
58
59 bool IsAsync() const { return async_state_ != nullptr; }
60
61 // Returns false if the cancelling failed, true otherwise.
62 bool CancelTimeoutTask();
63
64 private:
65 friend class FutexEmulation;
66 friend class FutexWaitList;
67
68 // Async wait requires substantially more information than synchronous wait.
69 // Hence store that additional information in a heap-allocated struct to make
70 // it more obvious that this will only be needed for the async case.
71 struct AsyncState {
72 AsyncState(Isolate* isolate, std::shared_ptr<TaskRunner> task_runner,
73 std::weak_ptr<BackingStore> backing_store,
79 promise(std::move(promise)),
81 DCHECK(this->promise.IsWeak());
82 DCHECK(this->native_context.IsWeak());
83 }
84
86 // Assert that the timeout task was cancelled.
88 }
89
91 std::shared_ptr<TaskRunner> const task_runner;
92
93 // The backing store on which we are waiting might die in an async wait.
94 // We keep a weak_ptr to verify during a wake operation that the original
95 // backing store is still mapped to that address.
96 std::weak_ptr<BackingStore> const backing_store;
97
98 // Weak Global handle. Must not be synchronously resolved by a non-owner
99 // Isolate.
101
102 // Weak Global handle.
104
105 // If timeout_time_ is base::TimeTicks(), this async waiter doesn't have a
106 // timeout or has already been notified. Values other than base::TimeTicks()
107 // are used for async waiters with an active timeout.
109
110 // The task ID of the timeout task.
113 };
114
116 // prev_ and next_ are protected by FutexEmulationGlobalState::mutex.
119
120 // The memory location the FutexWaitListNode is waiting on. Equals
121 // backing_store_->buffer_start() + wait_addr at FutexWaitListNode creation
122 // time. This address is used find the node in the per-location list, or to
123 // remove it.
124 // Note that during an async wait the BackingStore might get deleted while
125 // this node is alive.
126 void* wait_location_ = nullptr;
127
128 // waiting_ and interrupted_ are protected by FutexEmulationGlobalState::mutex
129 // if this node is currently contained in
130 // FutexEmulationGlobalState::wait_list.
131 bool waiting_ = false;
132 bool interrupted_ = false;
133
134 // State used for an async wait; nullptr on sync waits.
135 const std::unique_ptr<AsyncState> async_state_;
136};
137
138class FutexEmulation : public AllStatic {
139 public:
140 enum WaitMode { kSync = 0, kAsync };
141 enum class CallType { kIsNotWasm = 0, kIsWasm };
142
143 // Pass to Wake() to wake all waiters.
144 static const uint32_t kWakeAll = UINT32_MAX;
145
146 // Check that array_buffer[addr] == value, and return "not-equal" if not. If
147 // they are equal, block execution on |isolate|'s thread until woken via
148 // |Wake|, or when the time given in |rel_timeout_ms| elapses. Note that
149 // |rel_timeout_ms| can be Infinity.
150 // If woken, return "ok", otherwise return "timed-out". The initial check and
151 // the decision to wait happen atomically.
152 static Tagged<Object> WaitJs32(Isolate* isolate, WaitMode mode,
153 DirectHandle<JSArrayBuffer> array_buffer,
154 size_t addr, int32_t value,
155 double rel_timeout_ms);
156
157 // An version of WaitJs32 for int64_t values.
158 static Tagged<Object> WaitJs64(Isolate* isolate, WaitMode mode,
159 DirectHandle<JSArrayBuffer> array_buffer,
160 size_t addr, int64_t value,
161 double rel_timeout_ms);
162
163 // Same as WaitJs above except it returns 0 (ok), 1 (not equal) and 2 (timed
164 // out) as expected by Wasm.
166 Isolate* isolate, DirectHandle<JSArrayBuffer> array_buffer, size_t addr,
167 int32_t value, int64_t rel_timeout_ns);
168
169 // Same as Wait32 above except it checks for an int64_t value in the
170 // array_buffer.
172 Isolate* isolate, DirectHandle<JSArrayBuffer> array_buffer, size_t addr,
173 int64_t value, int64_t rel_timeout_ns);
174
175 // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
176 // |num_waiters_to_wake| can be kWakeAll, in which case all waiters are
177 // woken. The rest of the waiters will continue to wait. The return value is
178 // the number of woken waiters.
179 // Variant 1: Compute the wait address from the |array_buffer| and |addr|.
180 V8_EXPORT_PRIVATE static int Wake(Tagged<JSArrayBuffer> array_buffer,
181 size_t addr, uint32_t num_waiters_to_wake);
182 // Variant 2: Pass raw |addr| (used for WebAssembly atomic.notify).
183 static int Wake(void* addr, uint32_t num_waiters_to_wake);
184
185 // Called before |isolate| dies. Removes async waiters owned by |isolate|.
186 static void IsolateDeinit(Isolate* isolate);
187
188 // Return the number of threads or async waiters waiting on |addr|. Should
189 // only be used for testing.
190 static int NumWaitersForTesting(Tagged<JSArrayBuffer> array_buffer,
191 size_t addr);
192
193 // Return the number of async waiters which were waiting for |addr| and are
194 // now waiting for the Promises to be resolved. Should only be used for
195 // testing.
197 Tagged<JSArrayBuffer> array_buffer, size_t addr);
198
199 private:
200 friend class FutexWaitListNode;
203
204 template <typename T>
205 static Tagged<Object> Wait(Isolate* isolate, WaitMode mode,
206 DirectHandle<JSArrayBuffer> array_buffer,
207 size_t addr, T value, double rel_timeout_ms);
208
209 template <typename T>
210 static Tagged<Object> Wait(Isolate* isolate, WaitMode mode,
211 DirectHandle<JSArrayBuffer> array_buffer,
212 size_t addr, T value, bool use_timeout,
213 int64_t rel_timeout_ns,
214 CallType call_type = CallType::kIsNotWasm);
215
216 template <typename T>
217 static Tagged<Object> WaitSync(Isolate* isolate,
218 DirectHandle<JSArrayBuffer> array_buffer,
219 size_t addr, T value, bool use_timeout,
220 int64_t rel_timeout_ns, CallType call_type);
221
222 template <typename T>
223 static Tagged<Object> WaitAsync(Isolate* isolate,
224 DirectHandle<JSArrayBuffer> array_buffer,
225 size_t addr, T value, bool use_timeout,
226 int64_t rel_timeout_ns, CallType call_type);
227
228 // Resolve the Promises of the async waiters which belong to |isolate|.
229 static void ResolveAsyncWaiterPromises(Isolate* isolate);
230
232
234
235 static void NotifyAsyncWaiter(FutexWaitListNode* node);
236
237 // Remove the node's Promise from the NativeContext's Promise set.
239};
240} // namespace internal
241} // namespace v8
242
243#endif // V8_EXECUTION_FUTEX_EMULATION_H_
V8_INLINE bool IsWeak() const
static void HandleAsyncWaiterTimeout(FutexWaitListNode *node)
static Tagged< Object > WaitSync(Isolate *isolate, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, T value, bool use_timeout, int64_t rel_timeout_ns, CallType call_type)
static Tagged< Object > Wait(Isolate *isolate, WaitMode mode, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, T value, double rel_timeout_ms)
static int NumUnresolvedAsyncPromisesForTesting(Tagged< JSArrayBuffer > array_buffer, size_t addr)
static int NumWaitersForTesting(Tagged< JSArrayBuffer > array_buffer, size_t addr)
static V8_EXPORT_PRIVATE Tagged< Object > WaitWasm64(Isolate *isolate, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, int64_t value, int64_t rel_timeout_ns)
static const uint32_t kWakeAll
static void NotifyAsyncWaiter(FutexWaitListNode *node)
static Tagged< Object > WaitAsync(Isolate *isolate, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, T value, bool use_timeout, int64_t rel_timeout_ns, CallType call_type)
static Tagged< Object > WaitJs64(Isolate *isolate, WaitMode mode, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, int64_t value, double rel_timeout_ms)
static Tagged< Object > WaitJs32(Isolate *isolate, WaitMode mode, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, int32_t value, double rel_timeout_ms)
static V8_EXPORT_PRIVATE Tagged< Object > WaitWasm32(Isolate *isolate, DirectHandle< JSArrayBuffer > array_buffer, size_t addr, int32_t value, int64_t rel_timeout_ns)
static void IsolateDeinit(Isolate *isolate)
static void ResolveAsyncWaiterPromise(FutexWaitListNode *node)
static void CleanupAsyncWaiterPromise(FutexWaitListNode *node)
static V8_EXPORT_PRIVATE int Wake(Tagged< JSArrayBuffer > array_buffer, size_t addr, uint32_t num_waiters_to_wake)
static void ResolveAsyncWaiterPromises(Isolate *isolate)
FutexWaitListNode(const FutexWaitListNode &)=delete
base::ConditionVariable cond_
const std::unique_ptr< AsyncState > async_state_
FutexWaitListNode & operator=(const FutexWaitListNode &)=delete
STL namespace.
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_EXPORT_PRIVATE
Definition macros.h:460
v8::Global< v8::Context > const native_context
std::shared_ptr< TaskRunner > const task_runner
AsyncState(Isolate *isolate, std::shared_ptr< TaskRunner > task_runner, std::weak_ptr< BackingStore > backing_store, v8::Global< v8::Promise > promise, v8::Global< v8::Context > native_context)
std::weak_ptr< BackingStore > const backing_store
v8::Global< v8::Promise > const promise