v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-atomics-synchronization.cc
Go to the documentation of this file.
1// Copyright 2022 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
8
9namespace v8 {
10namespace internal {
11namespace {
12
13std::optional<base::TimeDelta> GetTimeoutDelta(
14 DirectHandle<Object> timeout_obj) {
15 double ms = Object::NumberValue(*timeout_obj);
16 if (!std::isnan(ms)) {
17 if (ms < 0) ms = 0;
18 if (ms <= static_cast<double>(std::numeric_limits<int64_t>::max())) {
19 return base::TimeDelta::FromMilliseconds(static_cast<int64_t>(ms));
20 }
21 }
22 return std::nullopt;
23}
24
25DirectHandle<JSPromise> UnlockAsyncLockedMutexFromPromiseHandler(
26 Isolate* isolate) {
27 DirectHandle<Context> context(isolate->context(), isolate);
28 DirectHandle<Object> mutex(
29 context->get(JSAtomicsMutex::kMutexAsyncContextSlot), isolate);
30 DirectHandle<Object> unlock_promise(
32 DirectHandle<Object> waiter_wrapper_obj(
34 isolate);
35
36 auto js_mutex = Cast<JSAtomicsMutex>(mutex);
37 auto js_unlock_promise = Cast<JSPromise>(unlock_promise);
38 auto async_locked_waiter_wrapper = Cast<Foreign>(waiter_wrapper_obj);
39 js_mutex->UnlockAsyncLockedMutex(isolate, async_locked_waiter_wrapper);
40 return js_unlock_promise;
41}
42
43} // namespace
44
45BUILTIN(AtomicsMutexConstructor) {
46 DCHECK(v8_flags.harmony_struct);
47 HandleScope scope(isolate);
48 return *isolate->factory()->NewJSAtomicsMutex();
49}
50
51BUILTIN(AtomicsMutexLock) {
52 DCHECK(v8_flags.harmony_struct);
53 constexpr char method_name[] = "Atomics.Mutex.lock";
54 HandleScope scope(isolate);
55
56 DirectHandle<Object> js_mutex_obj = args.atOrUndefined(isolate, 1);
57 if (!IsJSAtomicsMutex(*js_mutex_obj)) {
59 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
60 isolate->factory()->NewStringFromAsciiChecked(
61 method_name)));
62 }
64 DirectHandle<Object> run_under_lock = args.atOrUndefined(isolate, 2);
65 if (!IsCallable(*run_under_lock)) {
67 isolate, NewTypeError(MessageTemplate::kNotCallable, run_under_lock));
68 }
69
70 // Like Atomics.wait, synchronous locking may block, and so is disallowed on
71 // the main thread.
72 //
73 // This is not a recursive lock, so also throw if recursively locking.
74 if (!isolate->allow_atomics_wait() || js_mutex->IsCurrentThreadOwner()) {
76 isolate, NewTypeError(MessageTemplate::kAtomicsOperationNotAllowed,
77 isolate->factory()->NewStringFromAsciiChecked(
78 method_name)));
79 }
80
82 {
83 JSAtomicsMutex::LockGuard lock_guard(isolate, js_mutex);
85 isolate, result,
86 Execution::Call(isolate, run_under_lock,
87 isolate->factory()->undefined_value(), {}));
88 }
89
90 return *result;
91}
92
93BUILTIN(AtomicsMutexTryLock) {
94 DCHECK(v8_flags.harmony_struct);
95 constexpr char method_name[] = "Atomics.Mutex.tryLock";
96 HandleScope scope(isolate);
97
98 DirectHandle<Object> js_mutex_obj = args.atOrUndefined(isolate, 1);
99 if (!IsJSAtomicsMutex(*js_mutex_obj)) {
101 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
102 isolate->factory()->NewStringFromAsciiChecked(
103 method_name)));
104 }
105 DirectHandle<JSAtomicsMutex> js_mutex = Cast<JSAtomicsMutex>(js_mutex_obj);
106 DirectHandle<Object> run_under_lock = args.atOrUndefined(isolate, 2);
107 if (!IsCallable(*run_under_lock)) {
109 isolate, NewTypeError(MessageTemplate::kNotCallable, run_under_lock));
110 }
111
112 DirectHandle<Object> callback_result;
113 bool success;
114 {
115 JSAtomicsMutex::TryLockGuard try_lock_guard(isolate, js_mutex);
116 if (try_lock_guard.locked()) {
118 isolate, callback_result,
119 Execution::Call(isolate, run_under_lock,
120 isolate->factory()->undefined_value(), {}));
121 success = true;
122 } else {
123 callback_result = isolate->factory()->undefined_value();
124 success = false;
125 }
126 }
128 JSAtomicsMutex::CreateResultObject(isolate, callback_result, success);
129 return *result;
130}
131
132BUILTIN(AtomicsMutexLockWithTimeout) {
133 DCHECK(v8_flags.harmony_struct);
134 constexpr char method_name[] = "Atomics.Mutex.lockWithTimeout";
135 HandleScope scope(isolate);
136
137 DirectHandle<Object> js_mutex_obj = args.atOrUndefined(isolate, 1);
138 if (!IsJSAtomicsMutex(*js_mutex_obj)) {
140 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
141 isolate->factory()->NewStringFromAsciiChecked(
142 method_name)));
143 }
144 DirectHandle<JSAtomicsMutex> js_mutex = Cast<JSAtomicsMutex>(js_mutex_obj);
145 DirectHandle<Object> run_under_lock = args.atOrUndefined(isolate, 2);
146 if (!IsCallable(*run_under_lock)) {
148 isolate, NewTypeError(MessageTemplate::kNotCallable, run_under_lock));
149 }
150
151 DirectHandle<Object> timeout_obj = args.atOrUndefined(isolate, 3);
152 std::optional<base::TimeDelta> timeout;
153 if (!IsNumber(*timeout_obj)) {
155 isolate, NewTypeError(MessageTemplate::kIsNotNumber, timeout_obj,
156 Object::TypeOf(isolate, timeout_obj)));
157 }
158 timeout = GetTimeoutDelta(timeout_obj);
159
160 // Like Atomics.wait, synchronous locking may block, and so is disallowed on
161 // the main thread.
162 //
163 // This is not a recursive lock, so also throw if recursively locking.
164 if (!isolate->allow_atomics_wait() || js_mutex->IsCurrentThreadOwner()) {
166 isolate, NewTypeError(MessageTemplate::kAtomicsOperationNotAllowed,
167 isolate->factory()->NewStringFromAsciiChecked(
168 method_name)));
169 }
170
171 DirectHandle<Object> callback_result;
172 bool success;
173 {
174 JSAtomicsMutex::LockGuard lock_guard(isolate, js_mutex, timeout);
175 if (V8_LIKELY(lock_guard.locked())) {
177 isolate, callback_result,
178 Execution::Call(isolate, run_under_lock,
179 isolate->factory()->undefined_value(), {}));
180 success = true;
181 } else {
182 callback_result = isolate->factory()->undefined_value();
183 success = false;
184 }
185 }
187 JSAtomicsMutex::CreateResultObject(isolate, callback_result, success);
188 return *result;
189}
190
191BUILTIN(AtomicsMutexLockAsync) {
192 DCHECK(v8_flags.harmony_struct);
193 constexpr char method_name[] = "Atomics.Mutex.lockAsync";
194 HandleScope scope(isolate);
195
196 DirectHandle<Object> js_mutex_obj = args.atOrUndefined(isolate, 1);
197 if (!IsJSAtomicsMutex(*js_mutex_obj)) {
199 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
200 isolate->factory()->NewStringFromAsciiChecked(
201 method_name)));
202 }
203 DirectHandle<JSAtomicsMutex> js_mutex = Cast<JSAtomicsMutex>(js_mutex_obj);
204 DirectHandle<Object> run_under_lock = args.atOrUndefined(isolate, 2);
205 if (!IsCallable(*run_under_lock)) {
207 isolate, NewTypeError(MessageTemplate::kNotCallable, run_under_lock));
208 }
209
210 DirectHandle<Object> timeout_obj = args.atOrUndefined(isolate, 3);
211 std::optional<base::TimeDelta> timeout = std::nullopt;
212 if (!IsUndefined(*timeout_obj, isolate)) {
213 if (!IsNumber(*timeout_obj)) {
215 isolate, NewTypeError(MessageTemplate::kIsNotNumber, timeout_obj,
216 Object::TypeOf(isolate, timeout_obj)));
217 }
218 timeout = GetTimeoutDelta(timeout_obj);
219 }
220
221 DirectHandle<JSPromise> result_promise;
223 isolate, result_promise,
224 JSAtomicsMutex::LockOrEnqueuePromise(isolate, js_mutex, run_under_lock,
225 timeout));
226
227 return *result_promise;
228}
229
230BUILTIN(AtomicsMutexAsyncUnlockResolveHandler) {
231 DCHECK(v8_flags.harmony_struct);
232 HandleScope scope(isolate);
233
234 DirectHandle<Object> previous_result = args.atOrUndefined(isolate, 1);
235 DirectHandle<JSPromise> js_unlock_promise =
236 UnlockAsyncLockedMutexFromPromiseHandler(isolate);
237
239 JSAtomicsMutex::CreateResultObject(isolate, previous_result, true);
240 auto resolve_result = JSPromise::Resolve(js_unlock_promise, result);
241 USE(resolve_result);
242 return *isolate->factory()->undefined_value();
243}
244
245BUILTIN(AtomicsMutexAsyncUnlockRejectHandler) {
246 DCHECK(v8_flags.harmony_struct);
247 HandleScope scope(isolate);
248
249 DirectHandle<Object> error = args.atOrUndefined(isolate, 1);
250 DirectHandle<JSPromise> js_unlock_promise =
251 UnlockAsyncLockedMutexFromPromiseHandler(isolate);
252
253 auto reject_result = JSPromise::Reject(js_unlock_promise, error);
254 USE(reject_result);
255 return *isolate->factory()->undefined_value();
256}
257
258BUILTIN(AtomicsConditionConstructor) {
259 DCHECK(v8_flags.harmony_struct);
260 HandleScope scope(isolate);
261 return *isolate->factory()->NewJSAtomicsCondition();
262}
263
264BUILTIN(AtomicsConditionWait) {
265 DCHECK(v8_flags.harmony_struct);
266 constexpr char method_name[] = "Atomics.Condition.wait";
267 HandleScope scope(isolate);
268
269 DirectHandle<Object> js_condition_obj = args.atOrUndefined(isolate, 1);
270 DirectHandle<Object> js_mutex_obj = args.atOrUndefined(isolate, 2);
271 DirectHandle<Object> timeout_obj = args.atOrUndefined(isolate, 3);
272 if (!IsJSAtomicsCondition(*js_condition_obj) ||
273 !IsJSAtomicsMutex(*js_mutex_obj)) {
275 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
276 isolate->factory()->NewStringFromAsciiChecked(
277 method_name)));
278 }
279
280 std::optional<base::TimeDelta> timeout = std::nullopt;
281 if (!IsUndefined(*timeout_obj, isolate)) {
282 if (!IsNumber(*timeout_obj)) {
284 isolate, NewTypeError(MessageTemplate::kIsNotNumber, timeout_obj,
285 Object::TypeOf(isolate, timeout_obj)));
286 }
287 timeout = GetTimeoutDelta(timeout_obj);
288 }
289
290 if (!isolate->allow_atomics_wait()) {
292 isolate, NewTypeError(MessageTemplate::kAtomicsOperationNotAllowed,
293 isolate->factory()->NewStringFromAsciiChecked(
294 method_name)));
295 }
296
297 auto js_condition = Cast<JSAtomicsCondition>(js_condition_obj);
298 auto js_mutex = Cast<JSAtomicsMutex>(js_mutex_obj);
299
300 if (!js_mutex->IsCurrentThreadOwner()) {
302 isolate,
303 NewTypeError(MessageTemplate::kAtomicsMutexNotOwnedByCurrentThread));
304 }
305
306 return isolate->heap()->ToBoolean(
307 JSAtomicsCondition::WaitFor(isolate, js_condition, js_mutex, timeout));
308}
309
310BUILTIN(AtomicsConditionNotify) {
311 DCHECK(v8_flags.harmony_struct);
312 constexpr char method_name[] = "Atomics.Condition.notify";
313 HandleScope scope(isolate);
314
315 DirectHandle<Object> js_condition_obj = args.atOrUndefined(isolate, 1);
316 DirectHandle<Object> count_obj = args.atOrUndefined(isolate, 2);
317 if (!IsJSAtomicsCondition(*js_condition_obj)) {
319 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
320 isolate->factory()->NewStringFromAsciiChecked(
321 method_name)));
322 }
323
324 uint32_t count;
325 if (IsUndefined(*count_obj, isolate)) {
327 } else {
328 double count_double;
330 isolate, count_double, Object::IntegerValue(isolate, count_obj));
331 if (count_double <= 0) {
332 return Smi::zero();
333 } else if (count_double > JSAtomicsCondition::kAllWaiters) {
334 count_double = JSAtomicsCondition::kAllWaiters;
335 }
336 count = static_cast<uint32_t>(count_double);
337 }
338
339 auto js_condition = Cast<JSAtomicsCondition>(js_condition_obj);
340 return *isolate->factory()->NewNumberFromUint(
341 JSAtomicsCondition::Notify(isolate, js_condition, count));
342}
343
344BUILTIN(AtomicsConditionWaitAsync) {
345 DCHECK(v8_flags.harmony_struct);
346 constexpr char method_name[] = "Atomics.Condition.waitAsync";
347 HandleScope scope(isolate);
348
349 DirectHandle<Object> js_condition_obj = args.atOrUndefined(isolate, 1);
350 DirectHandle<Object> js_mutex_obj = args.atOrUndefined(isolate, 2);
351 if (!IsJSAtomicsCondition(*js_condition_obj) ||
352 !IsJSAtomicsMutex(*js_mutex_obj)) {
354 isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
355 isolate->factory()->NewStringFromAsciiChecked(
356 method_name)));
357 }
358
359 DirectHandle<Object> timeout_obj = args.atOrUndefined(isolate, 3);
360 std::optional<base::TimeDelta> timeout = std::nullopt;
361 if (!IsUndefined(*timeout_obj, isolate)) {
362 if (!IsNumber(*timeout_obj)) {
364 isolate, NewTypeError(MessageTemplate::kIsNotNumber, timeout_obj,
365 Object::TypeOf(isolate, timeout_obj)));
366 }
367 timeout = GetTimeoutDelta(timeout_obj);
368 }
369
371 Cast<JSAtomicsCondition>(js_condition_obj);
372 auto js_mutex = Cast<JSAtomicsMutex>(js_mutex_obj);
373
374 if (!js_mutex->IsCurrentThreadOwner()) {
376 isolate,
377 NewTypeError(MessageTemplate::kAtomicsMutexNotOwnedByCurrentThread));
378 }
379
380 DirectHandle<JSReceiver> result_promise;
382 isolate, result_promise,
383 JSAtomicsCondition::WaitAsync(isolate, js_condition, js_mutex, timeout));
384 return *result_promise;
385}
386
387BUILTIN(AtomicsConditionAcquireLock) {
388 DCHECK(v8_flags.harmony_struct);
389 HandleScope scope(isolate);
390
391 DirectHandle<Context> context(isolate->context(), isolate);
392 DirectHandle<Object> js_mutex_obj(
393 context->get(JSAtomicsCondition::kMutexAsyncContextSlot), isolate);
394 DirectHandle<JSAtomicsMutex> js_mutex = Cast<JSAtomicsMutex>(js_mutex_obj);
395 DirectHandle<JSPromise> lock_promise =
397 return *lock_promise;
398}
399
400} // namespace internal
401} // namespace v8
#define BUILTIN(name)
static constexpr TimeDelta FromMilliseconds(int64_t milliseconds)
Definition time.h:84
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > Call(Isolate *isolate, DirectHandle< Object > callable, DirectHandle< Object > receiver, base::Vector< const DirectHandle< Object > > args)
Definition execution.cc:523
static V8_EXPORT_PRIVATE MaybeDirectHandle< JSReceiver > WaitAsync(Isolate *requester, DirectHandle< JSAtomicsCondition > cv, DirectHandle< JSAtomicsMutex > mutex, std::optional< base::TimeDelta > timeout)
static V8_EXPORT_PRIVATE uint32_t Notify(Isolate *requester, DirectHandle< JSAtomicsCondition > cv, uint32_t count)
static V8_EXPORT_PRIVATE bool WaitFor(Isolate *requester, DirectHandle< JSAtomicsCondition > cv, DirectHandle< JSAtomicsMutex > mutex, std::optional< base::TimeDelta > timeout)
static DirectHandle< JSPromise > LockAsyncWrapperForWait(Isolate *requester, DirectHandle< JSAtomicsMutex > mutex)
static DirectHandle< JSObject > CreateResultObject(Isolate *isolate, DirectHandle< Object > value, bool success)
static MaybeDirectHandle< JSPromise > LockOrEnqueuePromise(Isolate *isolate, DirectHandle< JSAtomicsMutex > mutex, DirectHandle< Object > callback, std::optional< base::TimeDelta > timeout)
static Handle< Object > Reject(DirectHandle< JSPromise > promise, DirectHandle< Object > reason, bool debug_event=true)
Definition objects.cc:5069
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > Resolve(DirectHandle< JSPromise > promise, DirectHandle< Object > resolution)
Definition objects.cc:5109
static Handle< String > TypeOf(Isolate *isolate, DirectHandle< Object > object)
Definition objects.cc:1001
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT Maybe< double > IntegerValue(Isolate *isolate, HandleType< T > input)
static constexpr Tagged< Smi > zero()
Definition smi.h:99
uint32_t count
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
#define THROW_NEW_ERROR_RETURN_FAILURE(isolate, call)
Definition isolate.h:294
#define MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:448
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
TNode< Context > context
ZoneVector< RpoNumber > & result
base::Mutex mutex
bool IsNumber(Tagged< Object > obj)
V8_EXPORT_PRIVATE FlagValues v8_flags
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define DCHECK(condition)
Definition logging.h:482
#define USE(...)
Definition macros.h:293
#define V8_LIKELY(condition)
Definition v8config.h:661