From de8376f704dd122b9baa77ebe7fc1fe5db6f8a32 Mon Sep 17 00:00:00 2001 From: Egor Porsev Date: Wed, 6 Sep 2023 18:20:57 +0300 Subject: [PATCH] Make SharedMemory data GC managed, refactor wait added wait64 add isLockFree Signed-off-by: Egor Porsev --- plugins/ets/runtime/ets_libbase_runtime.yaml | 50 ++++------ plugins/ets/runtime/ets_vm.cpp | 14 --- plugins/ets/runtime/ets_vm.h | 4 - .../runtime/intrinsics/escompat_Atomics.cpp | 17 ++++ plugins/ets/runtime/types/ets_array.h | 9 +- .../ets/runtime/types/ets_shared_memory.cpp | 82 ++++++++++------ plugins/ets/runtime/types/ets_shared_memory.h | 29 +++--- plugins/ets/stdlib/escompat/ArrayBuffer.ets | 98 ++++++++++--------- plugins/ets/stdlib/escompat/Atomics.ets | 70 ++++++++----- .../ets_test_suite/atomics/CMakeLists.txt | 2 + .../nonconcurrent_wait_i32_not_equal.ets | 32 ++++++ .../nonconcurrent_wait_i64_not_equal.ets | 32 ++++++ 12 files changed, 274 insertions(+), 165 deletions(-) create mode 100644 plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i32_not_equal.ets create mode 100644 plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i64_not_equal.ets diff --git a/plugins/ets/runtime/ets_libbase_runtime.yaml b/plugins/ets/runtime/ets_libbase_runtime.yaml index 6b9abeb21..1aef8f8de 100644 --- a/plugins/ets/runtime/ets_libbase_runtime.yaml +++ b/plugins/ets/runtime/ets_libbase_runtime.yaml @@ -1431,36 +1431,6 @@ intrinsics: args: [ i32 ] impl: panda::ets::intrinsics::SharedMemoryCreate - - name: SharedMemoryAt - space: ets - class_name: escompat.SharedMemory - method_name: at - static: false - signature: - ret: i8 - args: [ i32 ] - impl: panda::ets::intrinsics::SharedMemoryAt - - - name: SharedMemorySet - space: ets - class_name: escompat.SharedMemory - method_name: set - static: false - signature: - ret: void - args: [ i32, i8 ] - impl: panda::ets::intrinsics::SharedMemorySet - - - name: SharedMemoryGetByteLength - space: ets - class_name: escompat.SharedMemory - method_name: getByteLength - static: false - signature: - ret: i32 - args: [ ] - impl: panda::ets::intrinsics::SharedMemoryGetByteLength - - name: SharedMemoryAddI8 space: ets class_name: escompat.SharedMemory @@ -1561,6 +1531,16 @@ intrinsics: args: [ i32, i32 ] impl: panda::ets::intrinsics::SharedMemoryWaitI32 + - name: SharedMemoryWaitI64 + space: ets + class_name: escompat.SharedMemory + method_name: atomicWaitI64 + static: false + signature: + ret: i32 + args: [ i32, i64 ] + impl: panda::ets::intrinsics::SharedMemoryWaitI64 + - name: SharedMemoryTimedWaitI32 space: ets class_name: escompat.SharedMemory @@ -1571,6 +1551,16 @@ intrinsics: args: [ i32, i32, i64 ] impl: panda::ets::intrinsics::SharedMemoryTimedWaitI32 + - name: SharedMemoryTimedWaitI64 + space: ets + class_name: escompat.SharedMemory + method_name: atomicTimedWaitI64 + static: false + signature: + ret: i32 + args: [ i32, i64, i64 ] + impl: panda::ets::intrinsics::SharedMemoryTimedWaitI64 + - name: SharedMemoryNotify space: ets class_name: escompat.SharedMemory diff --git a/plugins/ets/runtime/ets_vm.cpp b/plugins/ets/runtime/ets_vm.cpp index d13a75d1a..5bf464d1f 100644 --- a/plugins/ets/runtime/ets_vm.cpp +++ b/plugins/ets/runtime/ets_vm.cpp @@ -176,8 +176,6 @@ PandaEtsVM::~PandaEtsVM() allocator->Delete(monitor_pool_); allocator->Delete(string_table_); allocator->Delete(compiler_); - // Atomic with seq_cst order reason: strong synchronization - allocator->Delete(atomics_shared_memory_.load(std::memory_order_seq_cst)); ASSERT(mm_ != nullptr); mm_->Finalize(); @@ -720,16 +718,4 @@ void PandaEtsVM::FirePromiseStateChanged(EtsHandle &promise) } } -PandaVector *PandaEtsVM::AllocateAtomicsSharedMemory(size_t byte_length) -{ - // Atomic with seq_cst order reason: strong synchronization - [[maybe_unused]] auto current_ptr = atomics_shared_memory_.load(std::memory_order_seq_cst); - ASSERT_PRINT(current_ptr == nullptr, "Currently, only a single SharedArrayBuffer is supported"); - // Note: the vector must be non-movable since it is shared between different threads - auto ptr = GetHeapManager()->GetInternalAllocator()->New>(byte_length, 0); - // Atomic with seq_cst order reason: strong synchronization - atomics_shared_memory_.store(ptr, std::memory_order_seq_cst); - return ptr; -} - } // namespace panda::ets diff --git a/plugins/ets/runtime/ets_vm.h b/plugins/ets/runtime/ets_vm.h index 38f016009..674f74842 100644 --- a/plugins/ets/runtime/ets_vm.h +++ b/plugins/ets/runtime/ets_vm.h @@ -297,8 +297,6 @@ public: return atomics_mutex_; } - PandaVector *AllocateAtomicsSharedMemory(size_t byte_length); - protected: bool CheckEntrypointSignature(Method *entrypoint) override; Expected InvokeEntrypointImpl(Method *entrypoint, @@ -359,8 +357,6 @@ private: std::function clear_interop_handle_scopes_; // for JS Atomics os::memory::Mutex atomics_mutex_; - // Shared memory for SharedArrayBuffer - std::atomic *> atomics_shared_memory_ {std::atomic(nullptr)}; ExternalData external_data_ {}; diff --git a/plugins/ets/runtime/intrinsics/escompat_Atomics.cpp b/plugins/ets/runtime/intrinsics/escompat_Atomics.cpp index ef0a44c46..3d2231549 100644 --- a/plugins/ets/runtime/intrinsics/escompat_Atomics.cpp +++ b/plugins/ets/runtime/intrinsics/escompat_Atomics.cpp @@ -140,6 +140,12 @@ extern "C" int32_t SharedMemoryWaitI32(EtsSharedMemory *mem, int32_t byte_offset return static_cast(result); } +extern "C" int32_t SharedMemoryWaitI64(EtsSharedMemory *mem, int32_t byte_offset, int64_t expected_value) +{ + auto result = mem->WaitI64(byte_offset, expected_value, std::nullopt); + return static_cast(result); +} + extern "C" int32_t SharedMemoryTimedWaitI32(EtsSharedMemory *mem, int32_t byte_offset, int32_t expected_value, int64_t ms) { @@ -149,6 +155,17 @@ extern "C" int32_t SharedMemoryTimedWaitI32(EtsSharedMemory *mem, int32_t byte_o return static_cast(result); } +extern "C" int32_t SharedMemoryTimedWaitI64(EtsSharedMemory *mem, int32_t byte_offset, int64_t expected_value, + int64_t ms) +{ + ASSERT(ms >= 0); + auto u_ms = static_cast(ms); + ScopedNativeCodeThread n(EtsCoroutine::GetCurrent()); + + auto result = mem->WaitI64(byte_offset, expected_value, std::optional(u_ms)); + return static_cast(result); +} + extern "C" int32_t SharedMemoryNotify(EtsSharedMemory *mem, int32_t byte_offset) { return mem->NotifyI32(byte_offset, std::nullopt); diff --git a/plugins/ets/runtime/types/ets_array.h b/plugins/ets/runtime/types/ets_array.h index f1b4c35a1..cec68b488 100644 --- a/plugins/ets/runtime/types/ets_array.h +++ b/plugins/ets/runtime/types/ets_array.h @@ -99,10 +99,11 @@ protected: } template - static T *CreateForPrimitive(EtsClassRoot root, uint32_t length) + static T *CreateForPrimitive(EtsClassRoot root, uint32_t length, + SpaceType space_type = SpaceType::SPACE_TYPE_OBJECT) { EtsClass *array_class = PandaEtsVM::GetCurrent()->GetClassLinker()->GetClassRoot(root); - return Create(array_class, length); + return Create(array_class, length, space_type); } template @@ -165,11 +166,11 @@ private: template class EtsPrimitiveArray : public EtsArray { public: - static EtsPrimitiveArray *Create(uint32_t length) + static EtsPrimitiveArray *Create(uint32_t length, SpaceType space_type = SpaceType::SPACE_TYPE_OBJECT) { ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS(); // NOLINTNEXTLINE(readability-magic-numbers) - return EtsArray::CreateForPrimitive(ETS_CLASS_ROOT, length); + return EtsArray::CreateForPrimitive(ETS_CLASS_ROOT, length, space_type); } void Set(uint32_t index, ClassType element) { diff --git a/plugins/ets/runtime/types/ets_shared_memory.cpp b/plugins/ets/runtime/types/ets_shared_memory.cpp index c46e1fe21..a52315787 100644 --- a/plugins/ets/runtime/types/ets_shared_memory.cpp +++ b/plugins/ets/runtime/types/ets_shared_memory.cpp @@ -52,7 +52,8 @@ EtsSharedMemory *EtsSharedMemory::Create(size_t length) auto cls = EtsCoroutine::GetCurrent()->GetPandaVM()->GetClassLinker()->GetSharedMemoryClass(); // Note: This object must be non-movable since the 'waiter_' pointer is shared between different threads auto mem = reinterpret_cast(EtsObject::CreateNonMovable(cls)); - mem->SetData(EtsCoroutine::GetCurrent()->GetPandaVM()->AllocateAtomicsSharedMemory(length)); + mem->array_ = EtsByteArray::Create(length, SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT); + ; mem->SetHeadWaiter(nullptr); return mem; } @@ -82,20 +83,23 @@ void EtsSharedMemory::UnlinkWaiter(Waiter &waiter) size_t EtsSharedMemory::GetLength() { - return GetData()->size(); + return array_->GetLength(); } -int8_t EtsSharedMemory::GetElement(int32_t index) +int8_t EtsSharedMemory::GetElement(uint32_t index) { - return GetData()->at(index); + ASSERT_PRINT(index < GetLength(), "SharedMemory index out of bounds"); + return array_->Get(index); } -void EtsSharedMemory::SetElement(int32_t index, int8_t element) +void EtsSharedMemory::SetElement(uint32_t index, int8_t element) { - GetData()->at(index) = element; + ASSERT_PRINT(index < GetLength(), "SharedMemory index out of bounds"); + array_->Set(index, element); } namespace { + std::string PrintWaiters(EtsHandle &buffer) { std::stringstream stream; @@ -113,50 +117,56 @@ bool IsLittleEndian() return *reinterpret_cast(&x) == static_cast(1); } -int32_t GetInt32FromBytesBig(const PandaVector &bytes, size_t index) +template +IntegerType AssembleFromBytes(EtsSharedMemory &mem, uint32_t index, uint32_t (*get_byte_index)(uint32_t, uint32_t)) { - uint32_t value = 0; - for (size_t i = 0; i < sizeof(int32_t); i++) { - auto cur_byte = bit_cast(static_cast(bytes.at(index + 3 - i))); + UIntegerType value = 0; + for (uint32_t i = 0; i < sizeof(IntegerType); i++) { + auto cur_byte_index = get_byte_index(index, i); + auto cur_byte = bit_cast(static_cast(mem.GetElement(cur_byte_index))); value |= cur_byte << (8U * i); } - return bit_cast(value); + return bit_cast(value); } -int32_t GetInt32FromBytesLittle(const PandaVector &bytes, size_t index) +uint32_t LittleEndianGetByteIndex(uint32_t index, uint32_t i) { - uint32_t value = 0; - for (size_t i = 0; i < sizeof(int32_t); i++) { - auto cur_byte = bit_cast(static_cast(bytes.at(index + i))); - value |= cur_byte << (8U * i); - } - return bit_cast(value); + return index + i; } -int32_t GetInt32FromBytes(const PandaVector &bytes, size_t index) +template +uint32_t BigEndianGetByteIndex(uint32_t index, uint32_t i) { - return IsLittleEndian() ? GetInt32FromBytesLittle(bytes, index) : GetInt32FromBytesBig(bytes, index); + return index + sizeof(IntegerType) - 1 - i; } -} // namespace -EtsSharedMemory::WaitResult EtsSharedMemory::WaitI32(int32_t byte_offset, int32_t expected_value, - std::optional timeout) +template +IntegerType AssembleFromBytes(EtsSharedMemory &mem, uint32_t index) +{ + return IsLittleEndian() + ? AssembleFromBytes(mem, index, LittleEndianGetByteIndex) + : AssembleFromBytes(mem, index, BigEndianGetByteIndex); +} + +template +EtsSharedMemory::WaitResult Wait(EtsSharedMemory *mem, uint32_t byte_offset, IntegerType expected_value, + std::optional timeout) { auto coroutine = EtsCoroutine::GetCurrent(); [[maybe_unused]] EtsHandleScope scope(coroutine); - EtsHandle this_handle(coroutine, this); + EtsHandle this_handle(coroutine, mem); ScopedNativeCodeThread n(coroutine); os::memory::LockHolder lock(coroutine->GetPandaVM()->GetAtomicsMutex()); ScopedManagedCodeThread m(coroutine); - auto witnessed_value = GetInt32FromBytes(*this_handle->GetData(), byte_offset); + auto witnessed_value = AssembleFromBytes(*mem, byte_offset); LOG(DEBUG, ATOMICS) << "Wait: witnesseed_value=" << witnessed_value << ", expected_value=" << expected_value; if (witnessed_value == expected_value) { // Only stack allocations // 1. Add waiter - auto waiter = Waiter(byte_offset); + auto waiter = EtsSharedMemory::Waiter(byte_offset); this_handle->LinkWaiter(waiter); LOG(DEBUG, ATOMICS) << "Wait: added waiter: " << reinterpret_cast(&waiter) << ", list: " << PrintWaiters(this_handle); @@ -174,14 +184,28 @@ EtsSharedMemory::WaitResult EtsSharedMemory::WaitI32(int32_t byte_offset, int32_ this_handle->UnlinkWaiter(waiter); LOG(DEBUG, ATOMICS) << "Wait: removed waiter: " << reinterpret_cast(&waiter) << ", list: " << PrintWaiters(this_handle); - return timed_out ? WaitResult::TIMED_OUT : WaitResult::OK; + return timed_out ? EtsSharedMemory::WaitResult::TIMED_OUT : EtsSharedMemory::WaitResult::OK; } LOG(DEBUG, ATOMICS) << "Wait: not-equal, returning"; - return WaitResult::NOT_EQUAL; + return EtsSharedMemory::WaitResult::NOT_EQUAL; +} + +} // namespace + +EtsSharedMemory::WaitResult EtsSharedMemory::WaitI32(uint32_t byte_offset, int32_t expected_value, + std::optional timeout) +{ + return Wait(this, byte_offset, expected_value, timeout); +} + +EtsSharedMemory::WaitResult EtsSharedMemory::WaitI64(uint32_t byte_offset, int64_t expected_value, + std::optional timeout) +{ + return Wait(this, byte_offset, expected_value, timeout); } -int32_t EtsSharedMemory::NotifyI32(int32_t byte_offset, std::optional count) +int32_t EtsSharedMemory::NotifyI32(uint32_t byte_offset, std::optional count) { auto coroutine = EtsCoroutine::GetCurrent(); [[maybe_unused]] EtsHandleScope scope(coroutine); diff --git a/plugins/ets/runtime/types/ets_shared_memory.h b/plugins/ets/runtime/types/ets_shared_memory.h index 066230673..c1f897fae 100644 --- a/plugins/ets/runtime/types/ets_shared_memory.h +++ b/plugins/ets/runtime/types/ets_shared_memory.h @@ -23,6 +23,7 @@ #include "libpandabase/macros.h" #include "libpandabase/mem/space.h" #include "libpandabase/os/mutex.h" +#include "libpandabase/mem/object_pointer.h" #include "plugins/ets/runtime/types/ets_class.h" #include "plugins/ets/runtime/types/ets_primitives.h" #include "plugins/ets/runtime/types/ets_object.h" @@ -68,34 +69,26 @@ public: waiter_ = reinterpret_cast(waiter); } - PandaVector *GetData() - { - return reinterpret_cast *>(data_); - } - - void SetData(PandaVector *data) - { - data_ = reinterpret_cast(data); - } - void LinkWaiter(Waiter &waiter); void UnlinkWaiter(Waiter &waiter); - int8_t GetElement(int32_t index); - void SetElement(int32_t index, int8_t element); + int8_t GetElement(uint32_t index); + void SetElement(uint32_t index, int8_t element); template std::pair ReadModifyWriteI8(int32_t index, const F &f); enum class WaitResult { OK = 0, NOT_EQUAL = 1, TIMED_OUT = 2 }; - WaitResult WaitI32(int32_t offset, int32_t expected_value, std::optional timeout); + WaitResult WaitI32(uint32_t offset, int32_t expected_value, std::optional timeout); + + WaitResult WaitI64(uint32_t offset, int64_t expected_value, std::optional timeout); - int32_t NotifyI32(int32_t offset, std::optional count); + int32_t NotifyI32(uint32_t offset, std::optional count); class Waiter { public: - explicit Waiter(int32_t offset) : offset_(offset) {} + explicit Waiter(uint32_t offset) : offset_(offset) {} bool Wait(std::optional timeout); @@ -113,7 +106,7 @@ public: notified_.store(true, std::memory_order_seq_cst); } - int32_t GetOffset() + uint32_t GetOffset() { return offset_; } @@ -141,14 +134,14 @@ public: private: os::memory::ConditionVariable cv_ {os::memory::ConditionVariable()}; std::atomic notified_ {std::atomic(false)}; - int32_t offset_; + uint32_t offset_; Waiter *prev_ {nullptr}; Waiter *next_ {nullptr}; }; private: - EtsLong data_; + ObjectPointer array_; EtsLong waiter_; }; diff --git a/plugins/ets/stdlib/escompat/ArrayBuffer.ets b/plugins/ets/stdlib/escompat/ArrayBuffer.ets index 52eaa78a3..6dc85654d 100644 --- a/plugins/ets/stdlib/escompat/ArrayBuffer.ets +++ b/plugins/ets/stdlib/escompat/ArrayBuffer.ets @@ -15,50 +15,6 @@ package escompat; -/** - * Internal non-movable memory of a SharedArrayBuffer - */ -class SharedMemory { - private dataPtr: long - private waiterPtr: long - - private constructor() {} - - internal native static create(byteLength: int): SharedMemory; - - internal native at(i: int): byte; - - internal native set(i: int, value: byte): void; - - internal native getByteLength(): int; - - internal native atomicAddI8(index: int, value: byte): byte; - - internal native atomicAndI8(index: int, value: byte): byte; - - internal native atomicCompareExchangeI8(index: int, expectedValue: byte, replacementValue: byte): byte; - - internal native atomicExchangeI8(index: int, value: byte): byte; - - internal native atomicLoadI8(index: int): byte; - - internal native atomicOrI8(index: int, value: byte): byte; - - internal native atomicStoreI8(index: int, value: byte): byte; - - internal native atomicSubI8(index: int, value: byte): byte; - - internal native atomicXorI8(index: int, value: byte): byte; - - internal native atomicWaitI32(byteOffset: int, value: int): int; - - internal native atomicTimedWaitI32(byteOffset: int, value: int, timeout: long): int; - - internal native atomicNotify(byteOffset: int): int; - - internal native atomicBoundedNotify(byteOffset: int, count: int): int; -} - /** * Either ArrayBuffer or SharedArrayBuffer */ @@ -203,6 +159,60 @@ export class ArrayBuffer extends Buffer public readonly byteLength: int } +/** + * Internal non-movable memory of a SharedArrayBuffer + */ +class SharedMemory { + private data: byte[] + private waiterPtr: long + + private constructor() {} + + internal native static create(byteLength: int): SharedMemory; + + internal at(i: int): byte { + return this.data[i] + } + + internal set(i: int, value: byte): void { + this.data[i] = value + } + + internal getByteLength(): int { + return this.data.length + } + + internal native atomicAddI8(index: int, value: byte): byte; + + internal native atomicAndI8(index: int, value: byte): byte; + + internal native atomicCompareExchangeI8(index: int, expectedValue: byte, replacementValue: byte): byte; + + internal native atomicExchangeI8(index: int, value: byte): byte; + + internal native atomicLoadI8(index: int): byte; + + internal native atomicOrI8(index: int, value: byte): byte; + + internal native atomicStoreI8(index: int, value: byte): byte; + + internal native atomicSubI8(index: int, value: byte): byte; + + internal native atomicXorI8(index: int, value: byte): byte; + + internal native atomicWaitI32(byteOffset: int, value: int): int; + + internal native atomicWaitI64(byteOffset: int, value: long): int; + + internal native atomicTimedWaitI32(byteOffset: int, value: int, timeout: long): int; + + internal native atomicTimedWaitI64(byteOffset: int, value: long, timeout: long): int; + + internal native atomicNotify(byteOffset: int): int; + + internal native atomicBoundedNotify(byteOffset: int, count: int): int; +} + export class SharedArrayBuffer extends Buffer { private sharedMemory: SharedMemory diff --git a/plugins/ets/stdlib/escompat/Atomics.ets b/plugins/ets/stdlib/escompat/Atomics.ets index 7b9aa7447..3e1dcc6f8 100644 --- a/plugins/ets/stdlib/escompat/Atomics.ets +++ b/plugins/ets/stdlib/escompat/Atomics.ets @@ -36,6 +36,25 @@ export class Atomics { } } + private static requireSharedMemory(buffer: Buffer): SharedMemory { + if (buffer instanceof ArrayBuffer) { + throw new TypeError("This method accepts only TypedArrays that view SharedArrayBuffers") + } else { + let mem = (buffer as SharedArrayBuffer).getSharedMemory() + return mem + } + } + + /** + * isLockFree(n) returns true if Atomic operations for typed arrays where "BYTER_PER_ELEMENT == n" + * use hardware atomics instructions instead of locks. + * + * Warning: currently, all Atomic operations use locks, but isLockFree(4) following the ECMA specification returns true. + */ + public isLockFree(byteSize: int): boolean { + return byteSize == 4 + } + // Int8Array public static add(typedArray: Int8Array, index: int, value: byte): byte { @@ -236,38 +255,28 @@ export class Atomics { } /** - * If typedArray[offset] != value suspends the current thread until it is notified by Atomics.notify. + * If "typedArray[offset] != value" suspends the current thread until it is notified by Atomics.notify. * - * Note: An Atomics.notify call will wake up this thread even if typedArray[offset] == value. + * Note: An Atomics.notify call will wake up this thread even if "typedArray[offset] == value". */ public static wait(typedArray: Int32Array, offset: int, value: int): string { let indexedPosition = Atomics.validateAtomicAccess(typedArray.byteOffset, Int32Array.BYTES_PER_ELEMENT, typedArray.length, offset) - let buffer = typedArray.buffer - if (buffer instanceof ArrayBuffer) { - throw new TypeError("Atomics.wait can only operate on a TypedArray that views a SharedArrayBuffer") - } else { - let mem = (buffer as SharedArrayBuffer).getSharedMemory() - let result = mem.atomicWaitI32(indexedPosition, value) - return Atomics.interpretWaitResult(result) - } + let mem = Atomics.requireSharedMemory(typedArray.buffer) + let result = mem.atomicWaitI32(indexedPosition, value) + return Atomics.interpretWaitResult(result) } /** - * If typedArray[offset] != value suspends the current thread until it is notified by Atomics.notify + * If "typedArray[offset] != value" suspends the current thread until it is notified by Atomics.notify * or until the given timeout passes. * - * Note: An Atomics.notify call will wake up this thread even if typedArray[offset] == value. + * Note: An Atomics.notify call will wake up this thread even if "typedArray[offset] == value". */ public static wait(typedArray: Int32Array, offset: int, value: int, timeout: long): string { let indexedPosition = Atomics.validateAtomicAccess(typedArray.byteOffset, Int32Array.BYTES_PER_ELEMENT, typedArray.length, offset) - let buffer = typedArray.buffer - if (buffer instanceof ArrayBuffer) { - throw new TypeError("Atomics.wait can only operate on a TypedArray that views a SharedArrayBuffer") - } else { - let mem = (buffer as SharedArrayBuffer).getSharedMemory() - let result = mem.atomicTimedWaitI32(indexedPosition, value, timeout) - return Atomics.interpretWaitResult(result) - } + let mem = Atomics.requireSharedMemory(typedArray.buffer) + let result = mem.atomicTimedWaitI64(indexedPosition, value, timeout) + return Atomics.interpretWaitResult(result) } /** @@ -340,12 +349,29 @@ export class Atomics { throw new Error("not implemented") } + /** + * If "typedArray[offset] != value" suspends the current thread until it is notified by Atomics.notify. + * + * Note: An Atomics.notify call will wake up this thread even if "typedArray[offset] == value". + */ public static wait(typedArray: BigInt64Array, offset: int, value: long): string { - throw new Error("not implemented") + let indexedPosition = Atomics.validateAtomicAccess(typedArray.byteOffset, Int32Array.BYTES_PER_ELEMENT, typedArray.length, offset) + let mem = Atomics.requireSharedMemory(typedArray.buffer) + let result = mem.atomicWaitI64(indexedPosition, value) + return Atomics.interpretWaitResult(result) } + /** + * If "typedArray[offset] != value" suspends the current thread until it is notified by Atomics.notify + * or until the given timeout passes. + * + * Note: An Atomics.notify call will wake up this thread even if "typedArray[offset] == value". + */ public static wait(typedArray: BigInt64Array, offset: int, value: long, timeout: long): string { - throw new Error("not implemented") + let indexedPosition = Atomics.validateAtomicAccess(typedArray.byteOffset, Int32Array.BYTES_PER_ELEMENT, typedArray.length, offset) + let mem = Atomics.requireSharedMemory(typedArray.buffer) + let result = mem.atomicTimedWaitI64(indexedPosition, value, timeout) + return Atomics.interpretWaitResult(result) } /** diff --git a/plugins/ets/tests/ets_test_suite/atomics/CMakeLists.txt b/plugins/ets/tests/ets_test_suite/atomics/CMakeLists.txt index 5e66dff9d..318d3ae33 100644 --- a/plugins/ets/tests/ets_test_suite/atomics/CMakeLists.txt +++ b/plugins/ets/tests/ets_test_suite/atomics/CMakeLists.txt @@ -26,6 +26,8 @@ set(atomics_tests # --- Non-concurrent wait/notify tests nonconcurrent_notify_zero nonconcurrent_wait_not_equal + nonconcurrent_wait_i32_not_equal + nonconcurrent_wait_i64_not_equal # --- Concurrent wait/notify tests concurrent_wait_store_notify diff --git a/plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i32_not_equal.ets b/plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i32_not_equal.ets new file mode 100644 index 000000000..6b439d6b5 --- /dev/null +++ b/plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i32_not_equal.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function test(byteToChange: int) { + let buf = new SharedArrayBuffer(4); + let arr = new Int32Array(buf, 0, 1); + let bytearr = new Int8Array(buf, 0, 4); + + (new Console()).println("Testing for byte: " + byteToChange) + Atomics.store(bytearr, byteToChange, 1 as byte); + let x = Atomics.wait(arr, 0, 0) + assert(x == "not-equal") +} + +function main() { + for (let i = 0; i < 4; i++) { + test(i) + } +} + diff --git a/plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i64_not_equal.ets b/plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i64_not_equal.ets new file mode 100644 index 000000000..6b439d6b5 --- /dev/null +++ b/plugins/ets/tests/ets_test_suite/atomics/nonconcurrent_wait_i64_not_equal.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function test(byteToChange: int) { + let buf = new SharedArrayBuffer(4); + let arr = new Int32Array(buf, 0, 1); + let bytearr = new Int8Array(buf, 0, 4); + + (new Console()).println("Testing for byte: " + byteToChange) + Atomics.store(bytearr, byteToChange, 1 as byte); + let x = Atomics.wait(arr, 0, 0) + assert(x == "not-equal") +} + +function main() { + for (let i = 0; i < 4; i++) { + test(i) + } +} + -- Gitee