Commit 2a1abac5 authored by Marja Hölttä's avatar Marja Hölttä Committed by Commit Bot

[Atomics.waitAsync] Implement Atomics.waitAsync

Original design doc:
https://docs.google.com/document/d/1dthXsVHMc1Sd_oYf9a-KZSFOd_a8dUgnt4REAG8YIXA

Design changes:
https://docs.google.com/document/d/1aeEGDm1XSqoJkQQKz9F75WqnuAa2caktxGy_O_KpO9Y


Bug: v8:10239
Change-Id: Iab94ccab85d7b4ff23cff1955774b42edf5be541
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2202981
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68844}
parent 1f4e8c7c
......@@ -746,6 +746,7 @@ namespace internal {
CPP(AtomicsNotify) \
CPP(AtomicsIsLockFree) \
CPP(AtomicsWait) \
CPP(AtomicsWaitAsync) \
CPP(AtomicsWake) \
\
/* String */ \
......
......@@ -177,25 +177,22 @@ BUILTIN(AtomicsNotify) {
RETURN_RESULT_OR_FAILURE(isolate, AtomicsWake(isolate, array, index, count));
}
// ES #sec-atomics.wait
// Atomics.wait( typedArray, index, value, timeout )
BUILTIN(AtomicsWait) {
HandleScope scope(isolate);
Handle<Object> array = args.atOrUndefined(isolate, 1);
Handle<Object> index = args.atOrUndefined(isolate, 2);
Handle<Object> value = args.atOrUndefined(isolate, 3);
Handle<Object> timeout = args.atOrUndefined(isolate, 4);
Object DoWait(Isolate* isolate, FutexEmulation::WaitMode mode,
Handle<Object> array, Handle<Object> index, Handle<Object> value,
Handle<Object> timeout) {
// 1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray, true).
Handle<JSTypedArray> sta;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, sta, ValidateSharedIntegerTypedArray(isolate, array, true));
// 2. Let i be ? ValidateAtomicAccess(typedArray, index).
Maybe<size_t> maybe_index = ValidateAtomicAccess(isolate, sta, index);
if (maybe_index.IsNothing()) return ReadOnlyRoots(isolate).exception();
size_t i = maybe_index.FromJust();
// According to the spec, we have to check value's type before
// looking at the timeout.
// 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
// 4. If arrayTypeName is "BigInt64Array", let v be ? ToBigInt64(value).
// 5. Otherwise, let v be ? ToInt32(value).
if (sta->type() == kExternalBigInt64Array) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
BigInt::FromObject(isolate, value));
......@@ -205,6 +202,8 @@ BUILTIN(AtomicsWait) {
Object::ToInt32(isolate, value));
}
// 6. Let q be ? ToNumber(timeout).
// 7. If q is NaN, let t be +∞, else let t be max(q, 0).
double timeout_number;
if (timeout->IsUndefined(isolate)) {
timeout_number = ReadOnlyRoots(isolate).infinity_value().Number();
......@@ -218,7 +217,11 @@ BUILTIN(AtomicsWait) {
timeout_number = 0;
}
if (!isolate->allow_atomics_wait()) {
// 8. If mode is sync, then
// a. Let B be AgentCanSuspend().
// b. If B is false, throw a TypeError exception.
if (mode == FutexEmulation::WaitMode::kSync &&
!isolate->allow_atomics_wait()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kAtomicsWaitNotAllowed));
}
......@@ -227,15 +230,39 @@ BUILTIN(AtomicsWait) {
if (sta->type() == kExternalBigInt64Array) {
return FutexEmulation::WaitJs64(
isolate, array_buffer, GetAddress64(i, sta->byte_offset()),
isolate, mode, array_buffer, GetAddress64(i, sta->byte_offset()),
Handle<BigInt>::cast(value)->AsInt64(), timeout_number);
} else {
DCHECK(sta->type() == kExternalInt32Array);
return FutexEmulation::WaitJs32(isolate, array_buffer,
return FutexEmulation::WaitJs32(isolate, mode, array_buffer,
GetAddress32(i, sta->byte_offset()),
NumberToInt32(*value), timeout_number);
}
}
// ES #sec-atomics.wait
// Atomics.wait( typedArray, index, value, timeout )
BUILTIN(AtomicsWait) {
HandleScope scope(isolate);
Handle<Object> array = args.atOrUndefined(isolate, 1);
Handle<Object> index = args.atOrUndefined(isolate, 2);
Handle<Object> value = args.atOrUndefined(isolate, 3);
Handle<Object> timeout = args.atOrUndefined(isolate, 4);
return DoWait(isolate, FutexEmulation::WaitMode::kSync, array, index, value,
timeout);
}
BUILTIN(AtomicsWaitAsync) {
HandleScope scope(isolate);
Handle<Object> array = args.atOrUndefined(isolate, 1);
Handle<Object> index = args.atOrUndefined(isolate, 2);
Handle<Object> value = args.atOrUndefined(isolate, 3);
Handle<Object> timeout = args.atOrUndefined(isolate, 4);
return DoWait(isolate, FutexEmulation::WaitMode::kAsync, array, index, value,
timeout);
}
} // namespace internal
} // namespace v8
This diff is collapsed.
......@@ -7,11 +7,16 @@
#include <stdint.h>
#include <map>
#include "include/v8.h"
#include "src/base/atomicops.h"
#include "src/base/lazy-instance.h"
#include "src/base/macros.h"
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/time.h"
#include "src/tasks/cancelable-task.h"
#include "src/utils/allocation.h"
// Support for emulating futexes, a low-level synchronization primitive. They
......@@ -50,47 +55,93 @@ class AtomicsWaitWakeHandle {
class FutexWaitListNode {
public:
FutexWaitListNode()
: prev_(nullptr),
next_(nullptr),
wait_addr_(0),
waiting_(false),
interrupted_(false) {}
// Create a sync FutexWaitListNode.
FutexWaitListNode() = default;
// Create an async FutexWaitListNode.
FutexWaitListNode(const std::shared_ptr<BackingStore>& backing_store,
size_t wait_addr, Handle<JSObject> promise_capability,
Isolate* isolate);
~FutexWaitListNode();
void NotifyWake();
bool IsAsync() const { return isolate_for_async_waiters_ != nullptr; }
// Returns false if the cancelling failed, true otherwise.
bool CancelTimeoutTask();
private:
friend class FutexEmulation;
friend class FutexWaitList;
friend class ResetWaitingOnScopeExit;
// Set only for async FutexWaitListNodes.
Isolate* isolate_for_async_waiters_ = nullptr;
std::shared_ptr<TaskRunner> task_runner_;
CancelableTaskManager* cancelable_task_manager_ = nullptr;
base::ConditionVariable cond_;
// prev_ and next_ are protected by FutexEmulation::mutex_.
FutexWaitListNode* prev_;
FutexWaitListNode* next_;
FutexWaitListNode* prev_ = nullptr;
FutexWaitListNode* next_ = nullptr;
std::weak_ptr<BackingStore> backing_store_;
size_t wait_addr_;
size_t wait_addr_ = 0;
// waiting_ and interrupted_ are protected by FutexEmulation::mutex_
// if this node is currently contained in FutexEmulation::wait_list_
// or an AtomicsWaitWakeHandle has access to it.
bool waiting_;
bool interrupted_;
bool waiting_ = false;
bool interrupted_ = false;
// Only for async FutexWaitListNodes. Weak Global handle. Must not be
// synchronously resolved by a non-owner Isolate.
v8::Global<v8::Promise> promise_;
// Only for async FutexWaitListNodes. Weak Global handle.
v8::Global<v8::Context> native_context_;
// Only for async FutexWaitListNodes. If async_timeout_time_ is
// base::TimeTicks(), this async waiter doesn't have a timeout or has already
// been notified. Values other than base::TimeTicks() are used for async
// waiters with an active timeout.
base::TimeTicks async_timeout_time_;
CancelableTaskManager::Id timeout_task_id_ =
CancelableTaskManager::kInvalidTaskId;
DISALLOW_COPY_AND_ASSIGN(FutexWaitListNode);
};
class FutexWaitList {
public:
FutexWaitList();
FutexWaitList() = default;
void AddNode(FutexWaitListNode* node);
void RemoveNode(FutexWaitListNode* node);
// For checking the internal consistency of the FutexWaitList.
void Verify();
// Verifies the local consistency of |node|. If it's the first node of its
// list, it must be |head|, and if it's the last node, it must be |tail|.
void VerifyNode(FutexWaitListNode* node, FutexWaitListNode* head,
FutexWaitListNode* tail);
// Returns true if |node| is on the linked list starting with |head|.
static bool NodeIsOnList(FutexWaitListNode* node, FutexWaitListNode* head);
private:
friend class FutexEmulation;
FutexWaitListNode* head_;
FutexWaitListNode* tail_;
FutexWaitListNode* head_ = nullptr;
FutexWaitListNode* tail_ = nullptr;
struct HeadAndTail {
FutexWaitListNode* head;
FutexWaitListNode* tail;
};
// Isolate* -> linked list of Nodes which are waiting for their Promises to
// be resolved.
std::map<Isolate*, HeadAndTail> isolate_promises_to_resolve_;
DISALLOW_COPY_AND_ASSIGN(FutexWaitList);
};
......@@ -108,6 +159,8 @@ class ResetWaitingOnScopeExit {
class FutexEmulation : public AllStatic {
public:
enum WaitMode { kSync = 0, kAsync };
// Pass to Wake() to wake all waiters.
static const uint32_t kWakeAll = UINT32_MAX;
......@@ -117,12 +170,14 @@ class FutexEmulation : public AllStatic {
// |rel_timeout_ms| can be Infinity.
// If woken, return "ok", otherwise return "timed-out". The initial check and
// the decision to wait happen atomically.
static Object WaitJs32(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
size_t addr, int32_t value, double rel_timeout_ms);
static Object WaitJs32(Isolate* isolate, WaitMode mode,
Handle<JSArrayBuffer> array_buffer, size_t addr,
int32_t value, double rel_timeout_ms);
// An version of WaitJs32 for int64_t values.
static Object WaitJs64(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
size_t addr, int64_t value, double rel_timeout_ms);
static Object WaitJs64(Isolate* isolate, WaitMode mode,
Handle<JSArrayBuffer> array_buffer, size_t addr,
int64_t value, double rel_timeout_ms);
// Same as WaitJs above except it returns 0 (ok), 1 (not equal) and 2 (timed
// out) as expected by Wasm.
......@@ -146,23 +201,64 @@ class FutexEmulation : public AllStatic {
size_t addr,
uint32_t num_waiters_to_wake);
// Return the number of threads waiting on |addr|. Should only be used for
// testing.
// Called before |isolate| dies. Removes async waiters owned by |isolate|.
static void IsolateDeinit(Isolate* isolate);
// Return the number of threads or async waiters waiting on |addr|. Should
// only be used for testing.
static Object NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
size_t addr);
// Return the number of async waiters (which belong to |isolate|) waiting.
// Should only be used for testing.
static Object NumAsyncWaitersForTesting(Isolate* isolate);
// Return the number of async waiters which were waiting for |addr| and are
// now waiting for the Promises to be resolved. Should only be used for
// testing.
static Object NumUnresolvedAsyncPromisesForTesting(
Handle<JSArrayBuffer> array_buffer, size_t addr);
private:
friend class FutexWaitListNode;
friend class AtomicsWaitWakeHandle;
friend class ResolveAsyncWaiterPromisesTask;
friend class AsyncWaiterTimeoutTask;
template <typename T>
static Object Wait(Isolate* isolate, WaitMode mode,
Handle<JSArrayBuffer> array_buffer, size_t addr, T value,
double rel_timeout_ms);
template <typename T>
static Object Wait(Isolate* isolate, WaitMode mode,
Handle<JSArrayBuffer> array_buffer, size_t addr, T value,
bool use_timeout, int64_t rel_timeout_ns);
template <typename T>
static Object Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
size_t addr, T value, double rel_timeout_ms);
static Object WaitSync(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
size_t addr, T value, bool use_timeout,
int64_t rel_timeout_ns);
template <typename T>
static Object Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
size_t addr, T value, bool use_timeout,
int64_t rel_timeout_ns);
static Object WaitAsync(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
size_t addr, T value, bool use_timeout,
int64_t rel_timeout_ns);
// Resolve the Promises of the async waiters which belong to |isolate|.
static void ResolveAsyncWaiterPromises(Isolate* isolate);
static void ResolveAsyncWaiterPromise(FutexWaitListNode* node);
static void HandleAsyncWaiterTimeout(FutexWaitListNode* node);
static void NotifyAsyncWaiter(FutexWaitListNode* node);
// Remove the node's Promise from the NativeContext's Promise set.
static void CleanupAsyncWaiterPromise(FutexWaitListNode* node);
// Deletes |node| and returns the next node of its list.
static FutexWaitListNode* DeleteAsyncWaiterNode(FutexWaitListNode* node);
// `mutex_` protects the composition of `wait_list_` (i.e. no elements may be
// added or removed without holding this mutex), as well as the `waiting_`
......
......@@ -2943,6 +2943,8 @@ void Isolate::Deinit() {
}
#endif // V8_OS_WIN64
FutexEmulation::IsolateDeinit(this);
debug()->Unload();
wasm_engine()->DeleteCompileJobsOnIsolate(this);
......
......@@ -223,7 +223,8 @@ DEFINE_IMPLICATION(harmony_weak_refs_with_cleanup_some, harmony_weak_refs)
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_weak_refs_with_cleanup_some, \
"harmony weak references with FinalizationRegistry.prototype.cleanupSome") \
V(harmony_regexp_match_indices, "harmony regexp match indices")
V(harmony_regexp_match_indices, "harmony regexp match indices") \
V(harmony_atomics_waitasync, "harmony Atomics.waitAsync")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \
......
......@@ -55,6 +55,7 @@
#include "src/objects/js-segmenter.h"
#endif // V8_INTL_SUPPORT
#include "src/objects/js-weak-refs.h"
#include "src/objects/ordered-hash-table.h"
#include "src/objects/property-cell.h"
#include "src/objects/slots-inl.h"
#include "src/objects/templates.h"
......@@ -4129,6 +4130,12 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(
#undef EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE
void Genesis::InitializeGlobal_harmony_atomics_waitasync() {
if (!FLAG_harmony_atomics_waitasync) return;
SimpleInstallFunction(isolate(), isolate()->atomics_object(), "waitAsync",
Builtins::kAtomicsWaitAsync, 4, true);
}
void Genesis::InitializeGlobal_harmony_sharedarraybuffer() {
if (!FLAG_harmony_sharedarraybuffer) return;
......@@ -4736,6 +4743,11 @@ bool Genesis::InstallABunchOfRandomThings() {
map->AppendDescriptor(isolate(), &d);
}
}
{
Handle<OrderedHashSet> promises =
OrderedHashSet::Allocate(isolate(), 0).ToHandleChecked();
native_context()->set_atomics_waitasync_promises(*promises);
}
return true;
}
......
......@@ -5,14 +5,14 @@
#ifndef V8_OBJECTS_CONTEXTS_INL_H_
#define V8_OBJECTS_CONTEXTS_INL_H_
#include "src/objects/contexts.h"
#include "src/heap/heap-write-barrier.h"
#include "src/objects/contexts.h"
#include "src/objects/dictionary-inl.h"
#include "src/objects/fixed-array-inl.h"
#include "src/objects/js-objects-inl.h"
#include "src/objects/map-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/ordered-hash-table-inl.h"
#include "src/objects/osr-optimized-code-cache-inl.h"
#include "src/objects/regexp-match-info.h"
#include "src/objects/scope-info.h"
......
......@@ -7,6 +7,7 @@
#include "src/objects/fixed-array.h"
#include "src/objects/function-kind.h"
#include "src/objects/ordered-hash-table.h"
#include "src/objects/osr-optimized-code-cache.h"
#include "torque-generated/field-offsets-tq.h"
// Has to be the last include (doesn't have include guards):
......@@ -231,6 +232,7 @@ enum ContextLookupFlags {
slow_object_with_object_prototype_map) \
V(SLOW_TEMPLATE_INSTANTIATIONS_CACHE_INDEX, SimpleNumberDictionary, \
slow_template_instantiations_cache) \
V(ATOMICS_WAITASYNC_PROMISES, OrderedHashSet, atomics_waitasync_promises) \
/* Fast Path Protectors */ \
V(REGEXP_SPECIES_PROTECTOR_INDEX, PropertyCell, regexp_species_protector) \
/* All *_FUNCTION_MAP_INDEX definitions used by Context::FunctionMapIndex */ \
......
......@@ -36,6 +36,28 @@ RUNTIME_FUNCTION(Runtime_AtomicsNumWaitersForTesting) {
return FutexEmulation::NumWaitersForTesting(array_buffer, addr);
}
RUNTIME_FUNCTION(Runtime_AtomicsNumAsyncWaitersForTesting) {
DCHECK_EQ(0, args.length());
return FutexEmulation::NumAsyncWaitersForTesting(isolate);
}
RUNTIME_FUNCTION(Runtime_AtomicsNumUnresolvedAsyncPromisesForTesting) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
CONVERT_SIZE_ARG_CHECKED(index, 1);
CHECK(!sta->WasDetached());
CHECK(sta->GetBuffer()->is_shared());
CHECK_LT(index, sta->length());
CHECK_EQ(sta->type(), kExternalInt32Array);
Handle<JSArrayBuffer> array_buffer = sta->GetBuffer();
size_t addr = (index << 2) + sta->byte_offset();
return FutexEmulation::NumUnresolvedAsyncPromisesForTesting(array_buffer,
addr);
}
RUNTIME_FUNCTION(Runtime_SetAllowAtomicsWait) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -51,17 +51,19 @@ namespace internal {
F(TransitionElementsKind, 2, 1) \
F(TransitionElementsKindWithKind, 2, 1)
#define FOR_EACH_INTRINSIC_ATOMICS(F, I) \
F(AtomicsLoad64, 2, 1) \
F(AtomicsStore64, 3, 1) \
F(AtomicsAdd, 3, 1) \
F(AtomicsAnd, 3, 1) \
F(AtomicsCompareExchange, 4, 1) \
F(AtomicsExchange, 3, 1) \
F(AtomicsNumWaitersForTesting, 2, 1) \
F(AtomicsOr, 3, 1) \
F(AtomicsSub, 3, 1) \
F(AtomicsXor, 3, 1) \
#define FOR_EACH_INTRINSIC_ATOMICS(F, I) \
F(AtomicsLoad64, 2, 1) \
F(AtomicsStore64, 3, 1) \
F(AtomicsAdd, 3, 1) \
F(AtomicsAnd, 3, 1) \
F(AtomicsCompareExchange, 4, 1) \
F(AtomicsExchange, 3, 1) \
F(AtomicsNumWaitersForTesting, 2, 1) \
F(AtomicsNumAsyncWaitersForTesting, 0, 1) \
F(AtomicsNumUnresolvedAsyncPromisesForTesting, 2, 1) \
F(AtomicsOr, 3, 1) \
F(AtomicsSub, 3, 1) \
F(AtomicsXor, 3, 1) \
F(SetAllowAtomicsWait, 1, 1)
#define FOR_EACH_INTRINSIC_BIGINT(F, I) \
......
......@@ -32,6 +32,7 @@ enum class TryAbortResult { kTaskRemoved, kTaskRunning, kTaskAborted };
class V8_EXPORT_PRIVATE CancelableTaskManager {
public:
using Id = uint64_t;
static constexpr Id kInvalidTaskId = 0;
CancelableTaskManager();
......@@ -68,8 +69,6 @@ class V8_EXPORT_PRIVATE CancelableTaskManager {
bool canceled() const { return canceled_; }
private:
static constexpr Id kInvalidTaskId = 0;
// Only called by {Cancelable} destructor. The task is done with executing,
// but needs to be removed.
void RemoveFinishedTask(Id id);
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
// Create a waiter with a long timeout.
const result_slow = Atomics.waitAsync(i32a, 0, 0, 200000);
// Create a waiter with a short timeout.
const result_fast = Atomics.waitAsync(i32a, 0, 0, 1);
assertEquals(true, result_slow.async);
assertEquals(true, result_fast.async);
assertEquals(2, %AtomicsNumWaitersForTesting(i32a, 0));
let slow_value = undefined;
result_slow.value.then(
(value) => { slow_value = value; },
() => { assertUnreachable(); });
let fast_value = undefined;
result_fast.value.then(
(value) => { fast_value = value; },
() => { assertUnreachable(); });
// Verify that the waiter with the short time out times out.
let rounds = 1000;
function continuation1() {
--rounds;
assertTrue(rounds > 0);
if (fast_value == undefined) {
setTimeout(continuation1, 0);
} else {
assertEquals("timed-out", fast_value);
// Wake up the waiter with the long time out.
let notify_return_value = Atomics.notify(i32a, 0, 1);
assertEquals(1, notify_return_value);
setTimeout(continuation2, 0);
}
}
function continuation2() {
assertEquals("ok", slow_value);
}
setTimeout(continuation1, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync --expose-gc --no-stress-opt
(function test() {
let timed_out = false;
(function() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
// Create a waiter with a timeout.
const result = Atomics.waitAsync(i32a, 0, 0, 1);
result.value.then(
(value) => { assertEquals('timed-out', value); timed_out = true; },
() => { assertUnreachable(); });
})();
// Make sure sab, ia32 and result get gc()d.
gc();
assertEquals(1, %AtomicsNumAsyncWaitersForTesting());
// Even if the buffer went out of scope, we keep the waitAsync alive so that it can still time out.
let resolved = false;
const sab2 = new SharedArrayBuffer(16);
const i32a2 = new Int32Array(sab2);
const result2 = Atomics.waitAsync(i32a2, 0, 0);
result2.value.then(
(value) => { assertEquals("ok", value); resolved = true; },
() => { assertUnreachable(); });
assertEquals(2, %AtomicsNumAsyncWaitersForTesting());
const notify_return_value = Atomics.notify(i32a2, 0);
assertEquals(1, notify_return_value);
assertEquals(1, %AtomicsNumAsyncWaitersForTesting());
// Verify that the waiter gets woken up.
let rounds = 1000;
function wait() {
--rounds;
assertTrue(rounds > 0);
if (!timed_out) {
setTimeout(wait, 0);
}
}
setTimeout(wait, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync --expose-gc --no-stress-opt
(function test() {
(function() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
const result = Atomics.waitAsync(i32a, 0, 0);
result.value.then(
(value) => { assertUnreachable(); },
() => { assertUnreachable(); });
})();
// Make sure sab, ia32 and result get gc()d.
gc();
assertEquals(1, %AtomicsNumAsyncWaitersForTesting());
// The next time iterate the waiter list, we clean up the waiter which can
// never be woken up.
let resolved = false;
const sab2 = new SharedArrayBuffer(16);
const i32a2 = new Int32Array(sab2);
const result2 = Atomics.waitAsync(i32a2, 0, 0);
result2.value.then(
(value) => { assertEquals("ok", value); resolved = true; },
() => { assertUnreachable(); });
assertEquals(2, %AtomicsNumAsyncWaitersForTesting());
const notify_return_value = Atomics.notify(i32a2, 0);
assertEquals(1, notify_return_value);
assertEquals(0, %AtomicsNumAsyncWaitersForTesting());
setTimeout(()=> {
assertTrue(resolved);
}, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync --expose-gc
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
let resolved = false;
(function() {
const result = Atomics.waitAsync(i32a, 0, 0);
result.value.then(
(value) => { assertEquals("ok", value); resolved = true; },
() => { assertUnreachable(); });
})();
// Make sure result gets gc()d.
gc();
const notify_return_value = Atomics.notify(i32a, 0, 1);
assertEquals(1, notify_return_value);
assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(1, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
setTimeout(()=> {
assertTrue(resolved);
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
}, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
// Create a waiter with a timeout.
const result = Atomics.waitAsync(i32a, 0, 0, 1);
assertEquals(true, result.async);
assertEquals(1, %AtomicsNumWaitersForTesting(i32a, 0));
let resolved = false;
result.value.then(
(value) => { assertEquals("timed-out", value); resolved = true; },
() => { assertUnreachable(); });
// Verify that the waiter gets woken up.
let rounds = 1000;
function wait() {
--rounds;
assertTrue(rounds > 0);
if (!resolved) {
setTimeout(wait, 0);
}
}
setTimeout(wait, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
const N = 10;
let log = [];
// Create N async waiters; the even ones without timeout and the odd ones
// with timeout.
for (let i = 0; i < N; ++i) {
let result;
if (i % 2 == 0) {
result = Atomics.waitAsync(i32a, 0, 0);
} else {
result = Atomics.waitAsync(i32a, 0, 0, i);
}
assertEquals(true, result.async);
result.value.then(
(value) => { log.push(value + " " + i); },
() => { assertUnreachable(); });
}
assertEquals(N, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
// Wait until the timed out waiters time out.
let rounds = 10000;
let previous_length = 0;
function wait() {
--rounds;
assertTrue(rounds > 0);
if (log.length > previous_length) {
// Made progress. Give the test more time.
previous_length = log.length;
rounds = 10000;
}
if (log.length < N / 2) {
setTimeout(wait, 0);
} else {
continuation1();
}
}
setTimeout(wait, 0);
function continuation1() {
// Verify that all timed out waiters timed out in FIFO order.
assertEquals(N / 2, log.length);
let waiter_no = 1;
for (let i = 0; i < N / 2; ++i) {
assertEquals("timed-out " + waiter_no, log[i]);
waiter_no += 2;
}
// Wake up all waiters
let notify_return_value = Atomics.notify(i32a, 0);
assertEquals(N / 2, notify_return_value);
assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(N / 2, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
setTimeout(continuation2, 0);
}
function continuation2() {
// Verify that the waiters woke up in FIFO order.
assertEquals(N, log.length);
let waiter_no = 0;
for (let i = N / 2; i < N; ++i) {
assertEquals("ok " + waiter_no, log[i]);
waiter_no += 2;
}
}
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
const N = 10;
let log = [];
// Create N async waiters.
for (let i = 0; i < N; ++i) {
const result = Atomics.waitAsync(i32a, 0, 0);
assertEquals(true, result.async);
result.value.then(
(value) => { assertEquals("ok", value); log.push(i); },
() => { assertUnreachable(); });
}
assertEquals(N, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
// Wake up all waiters.
let notify_return_value = Atomics.notify(i32a, 0);
assertEquals(N, notify_return_value);
assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(N, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
function continuation() {
assertEquals(N, log.length);
for (let i = 0; i < N; ++i) {
assertEquals(i, log[i]);
}
}
setTimeout(continuation, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
// Create 2 async waiters.
const result1 = Atomics.waitAsync(i32a, 0, 0);
const result2 = Atomics.waitAsync(i32a, 0, 0);
assertEquals(true, result1.async);
assertEquals(true, result2.async);
assertEquals(2, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
let log = [];
result1.value.then(
(value) => { assertEquals("ok", value); log.push(1); },
() => { assertUnreachable(); });
result2.value.then(
(value) => { assertEquals("ok", value); log.push(2); },
() => { assertUnreachable(); });
// Wake up one waiter.
const notify_return_value = Atomics.notify(i32a, 0, 1);
assertEquals(1, notify_return_value);
assertEquals(1, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(1, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
function continuation1() {
assertEquals(1, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
assertEquals([1], log);
// Wake up one waiter.
const notify_return_value = Atomics.notify(i32a, 0, 1);
assertEquals(1, notify_return_value);
assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(1, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
setTimeout(continuation2, 0);
}
function continuation2() {
assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
assertEquals([1, 2], log);
}
setTimeout(continuation1, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
const result = Atomics.waitAsync(i32a, 0, 0);
assertEquals(true, result.async);
assertTrue(result.value instanceof Promise);
assertEquals(1, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
let resolved = false;
result.value.then(
(value) => { assertEquals("ok", value); resolved = true; },
() => { assertUnreachable(); });
const notify_return_value = Atomics.notify(i32a, 0, 1);
assertEquals(1, notify_return_value);
assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0));
assertEquals(1, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
setTimeout(()=> {
assertTrue(resolved);
assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0));
}, 0);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync --expose-gc
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
(function createWorker() {
const script = `onmessage = function(msg) {
if (msg.sab) {
const i32a = new Int32Array(msg.sab);
const result = Atomics.waitAsync(i32a, 0, 0);
postMessage('worker waiting');
}
}`;
const w = new Worker(script, {type : 'string'});
w.postMessage({sab: sab});
const m = w.getMessage();
assertEquals('worker waiting', m);
w.terminate();
})();
gc();
Atomics.notify(i32a, 0, 1);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync --expose-gc
(function test() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
(function createWorker() {
const script = `onmessage = function(msg) {
if (msg.sab) {
const i32a = new Int32Array(msg.sab);
const result = Atomics.waitAsync(i32a, 0, 0, 100000);
postMessage('worker waiting');
}
}`;
const w = new Worker(script, {type : 'string'});
w.postMessage({sab: sab});
const m = w.getMessage();
assertEquals('worker waiting', m);
w.terminate();
})();
gc();
Atomics.notify(i32a, 0, 1);
})();
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-sharedarraybuffer --harmony-atomics-waitasync
(function testOutOfBounds() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
assertThrows(() => {
Atomics.waitAsync(i32a, 20, 0, 1000);
}, RangeError);
})();
(function testValueNotEquals() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
const result = Atomics.waitAsync(i32a, 0, 1, 1000);
assertEquals(false, result.async);
assertEquals("not-equal", result.value);
})();
(function testZeroTimeout() {
const sab = new SharedArrayBuffer(16);
const i32a = new Int32Array(sab);
const result = Atomics.waitAsync(i32a, 0, 0, 0);
assertEquals(false, result.async);
assertEquals("timed-out", result.value);
})();
......@@ -842,6 +842,17 @@
# Tier down/up Wasm NativeModule in debugging is non-deterministic with
# multiple isolates (https://crbug.com/v8/10099).
'wasm/tier-down-to-liftoff': [SKIP],
# waitAsync tests modify the global state (across Isolates)
'harmony/atomics-waitasync': [SKIP],
'harmony/atomics-waitasync-1thread-2timeout': [SKIP],
'harmony/atomics-waitasync-1thread-buffer-out-of-scope': [SKIP],
'harmony/atomics-waitasync-1thread-promise-out-of-scope': [SKIP],
'harmony/atomics-waitasync-1thread-timeout': [SKIP],
'harmony/atomics-waitasync-1thread-wake-up-fifo': [SKIP],
'harmony/atomics-waitasync-1thread-wake-up-simple': [SKIP],
'harmony/atomics-waitasync-worker-shutdown-before-wait-finished-timeout': [SKIP],
'harmony/atomics-waitasync-worker-shutdown-before-wait-finished-no-timeout': [SKIP],
}], # 'isolates'
##############################################################################
......
......@@ -533,109 +533,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=10383
'built-ins/RegExp/prototype/Symbol.replace/fn-invoke-args-empty-result': [FAIL],
# http://crbug/v8/10239
'built-ins/Atomics/waitAsync/bad-range': [FAIL],
'built-ins/Atomics/waitAsync/bigint/bad-range': [FAIL],
'built-ins/Atomics/waitAsync/bigint/false-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/bigint/false-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/good-views': [FAIL],
'built-ins/Atomics/waitAsync/bigint/nan-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/negative-index-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/negative-timeout': [FAIL],
'built-ins/Atomics/waitAsync/bigint/negative-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-no-operation': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-add': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-and': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-compareExchange': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-exchange': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-or': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-store': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-sub': [FAIL],
'built-ins/Atomics/waitAsync/bigint/no-spurious-wakeup-on-xor': [FAIL],
'built-ins/Atomics/waitAsync/bigint/non-bigint64-typedarray-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/non-shared-bufferdata-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/not-a-typedarray-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/not-an-object-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/null-bufferdata-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/null-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/bigint/null-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/object-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/bigint/object-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/out-of-range-index-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/poisoned-object-for-timeout-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/poisoned-object-for-timeout-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/symbol-for-index-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/symbol-for-index-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/symbol-for-timeout-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/symbol-for-timeout-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/symbol-for-value-throws': [FAIL],
'built-ins/Atomics/waitAsync/bigint/symbol-for-value-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/true-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/bigint/true-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/undefined-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/bigint/undefined-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/undefined-index-defaults-to-zero-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/value-not-equal': [FAIL],
'built-ins/Atomics/waitAsync/bigint/value-not-equal-agent': [FAIL],
'built-ins/Atomics/waitAsync/bigint/waiterlist-block-indexedposition-wake': [FAIL],
'built-ins/Atomics/waitAsync/bigint/was-woken-before-timeout': [FAIL],
'built-ins/Atomics/waitAsync/descriptor': [FAIL],
'built-ins/Atomics/waitAsync/false-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/is-function': [FAIL],
'built-ins/Atomics/waitAsync/length': [FAIL],
'built-ins/Atomics/waitAsync/name': [FAIL],
'built-ins/Atomics/waitAsync/nan-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/negative-index-throws': [FAIL],
'built-ins/Atomics/waitAsync/negative-timeout': [FAIL],
'built-ins/Atomics/waitAsync/non-int32-typedarray-throws': [FAIL],
'built-ins/Atomics/waitAsync/non-shared-bufferdata-throws': [FAIL],
'built-ins/Atomics/waitAsync/not-a-typedarray-throws': [FAIL],
'built-ins/Atomics/waitAsync/not-an-object-throws': [FAIL],
'built-ins/Atomics/waitAsync/null-bufferdata-throws': [FAIL],
'built-ins/Atomics/waitAsync/null-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/object-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/out-of-range-index-throws': [FAIL],
'built-ins/Atomics/waitAsync/poisoned-object-for-timeout-throws': [FAIL],
'built-ins/Atomics/waitAsync/poisoned-object-for-timeout-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/returns-result-object-value-is-promise-resolves-to-ok': [FAIL],
'built-ins/Atomics/waitAsync/returns-result-object-value-is-promise-resolves-to-timed-out': [FAIL],
'built-ins/Atomics/waitAsync/returns-result-object-value-is-string-not-equal': [FAIL],
'built-ins/Atomics/waitAsync/returns-result-object-value-is-string-timed-out': [FAIL],
'built-ins/Atomics/waitAsync/symbol-for-index-throws': [FAIL],
'built-ins/Atomics/waitAsync/symbol-for-index-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/symbol-for-timeout-throws': [FAIL],
'built-ins/Atomics/waitAsync/symbol-for-timeout-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/symbol-for-value-throws': [FAIL],
'built-ins/Atomics/waitAsync/symbol-for-value-throws-agent': [FAIL],
'built-ins/Atomics/waitAsync/true-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/undefined-for-timeout-agent': [FAIL],
'built-ins/Atomics/waitAsync/undefined-for-timeout': [FAIL],
'built-ins/Atomics/waitAsync/undefined-index-defaults-to-zero-agent': [FAIL],
'built-ins/Atomics/waitAsync/validate-arraytype-before-index-coercion': [FAIL],
'built-ins/Atomics/waitAsync/validate-arraytype-before-timeout-coercion': [FAIL],
'built-ins/Atomics/waitAsync/validate-arraytype-before-value-coercion': [FAIL],
'built-ins/Atomics/waitAsync/value-not-equal': [FAIL],
'built-ins/Atomics/waitAsync/waiterlist-block-indexedposition-wake': [FAIL],
'built-ins/Atomics/waitAsync/was-woken-before-timeout': [FAIL],
# SKIP the following TIMEOUT tests instead of FAIL
'built-ins/Atomics/waitAsync/false-for-timeout-agent': [SKIP],
'built-ins/Atomics/waitAsync/good-views': [SKIP],
'built-ins/Atomics/waitAsync/negative-timeout-agent': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-no-operation': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-add': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-and': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-compareExchange': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-exchange': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-or': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-store': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-sub': [SKIP],
'built-ins/Atomics/waitAsync/no-spurious-wakeup-on-xor': [SKIP],
'built-ins/Atomics/waitAsync/null-for-timeout-agent': [SKIP],
'built-ins/Atomics/waitAsync/object-for-timeout-agent': [SKIP],
'built-ins/Atomics/waitAsync/true-for-timeout-agent': [SKIP],
'built-ins/Atomics/waitAsync/value-not-equal-agent': [SKIP],
# https://crbug.com/v8/10687
'built-ins/Atomics/add/bigint/non-shared-bufferdata': [FAIL],
'built-ins/Atomics/add/non-shared-bufferdata': [FAIL],
......
......@@ -65,6 +65,7 @@ FEATURE_FLAGS = {
'AggregateError': '--harmony-promise-any',
'logical-assignment-operators': '--harmony-logical-assignment',
'Promise.any': '--harmony-promise-any',
'Atomics.waitAsync': '--harmony-atomics-waitasync',
}
SKIPPED_FEATURES = set([])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment