diff --git a/static_core/BUILD.gn b/static_core/BUILD.gn index bc0d95372298852169c0f9348ac2e69e1768c956..869b8f59be2a96f6abc43b1a6172e1ad52d86c39 100644 --- a/static_core/BUILD.gn +++ b/static_core/BUILD.gn @@ -157,6 +157,7 @@ config("ark_config") { include_dirs = [ "$ark_root", + "$ark_root/libvsync/include", get_label_info(":create_pipeline(${default_toolchain})", "target_gen_dir"), ] defines = [ "PANDA_TARGET_MOBILE_WITH_MANAGED_LIBS=1" ] diff --git a/static_core/CMakeLists.txt b/static_core/CMakeLists.txt index 8941b0355adedd1d502e746d738cad253783e4f0..ec06ebbc92b02745c2d0b16c7e968656fa1af6f4 100644 --- a/static_core/CMakeLists.txt +++ b/static_core/CMakeLists.txt @@ -14,6 +14,8 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(PANDA NONE) +include_directories(SYSTEM libvsync/include) + # Add our custom configuration types to # multi-configuration generators (i.e. Visual Studio): if(CMAKE_CONFIGURATION_TYPES) diff --git a/static_core/codecheck_ignore.json b/static_core/codecheck_ignore.json index 3a6bbf74f38b0fcf8e03fa05df777373b63b25d8..635c25c631f31e6b3bf0188eb0dcc7e2f6d884cc 100644 --- a/static_core/codecheck_ignore.json +++ b/static_core/codecheck_ignore.json @@ -218,5 +218,6 @@ "compiler/optimizer/ir/visitor.inc": "*", "libllvmbackend/lowering/llvm_ir_constructor.cpp": {"G.FMT.13-CPP": [1]}, "plugins/ets/runtime/intrinsics/escompat_TypedArrays.cpp": {"G.FMT.02-CPP": "*"}, + "libvsync": "*", "*": ["G.CMT.03", "G.FMT.03-CPP", "G.FMT.04", "G.FMT.11", "G.NAM.03", "huge_headerfile", "huge_non_headerfile", "bc-30001"] } diff --git a/static_core/libvsync/include/vsync/atomic.h b/static_core/libvsync/include/vsync/atomic.h new file mode 100644 index 0000000000000000000000000000000000000000..290f456e2468aaadf066be2658a4e81dc6711abd --- /dev/null +++ b/static_core/libvsync/include/vsync/atomic.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025 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. + */ +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. + * SPDX-License-Identifier: MIT + */ + +/* + * This is a partial implementation of vatomic interface (https://github.com/open-s4c/vatomic). + * Please refer to the project for documentation. + */ + +#define V_COMPILER_BARRIER() __asm__ __volatile__("" ::: "memory") +#define await_while(cond) while (cond) + +#define V_ALIGNED4 __attribute__((aligned(4))) +typedef uint32_t vuint32_t; + +/* 32-bit unsigned atomic type, aligned at 4 bytes. */ +typedef struct vatomic32_s { + vuint32_t _v; +} V_ALIGNED4 vatomic32_t; + +#define vatomic_cpu_pause() \ + do { \ + } while (0) + +static inline void vatomic32_write(vatomic32_t *a, vuint32_t v) +{ + V_COMPILER_BARRIER(); + __atomic_store_n(&a->_v, v, __ATOMIC_SEQ_CST); + V_COMPILER_BARRIER(); +} + +static inline void vatomic32_init(vatomic32_t *a, vuint32_t v) +{ + vatomic32_write(a, v); +} + +static inline vuint32_t vatomic32_read_acq(const vatomic32_t *a) +{ + V_COMPILER_BARRIER(); + vuint32_t tmp = (vuint32_t)__atomic_load_n(&a->_v, __ATOMIC_ACQUIRE); + V_COMPILER_BARRIER(); + return tmp; +} + +static inline vuint32_t vatomic32_read_rlx(const vatomic32_t *a) +{ + V_COMPILER_BARRIER(); + vuint32_t tmp = (vuint32_t)__atomic_load_n(&a->_v, __ATOMIC_RELAXED); + V_COMPILER_BARRIER(); + return tmp; +} + +static inline vuint32_t vatomic32_await_eq_rlx(const vatomic32_t *a, vuint32_t c) +{ + vuint32_t ret = c; + vuint32_t o = 0; + await_while((o = vatomic32_read_rlx(a)) != c) + { + vatomic_cpu_pause(); + ret = o; + } + return ret; +} + +static inline void vatomic32_write_rel(vatomic32_t *a, vuint32_t v) +{ + V_COMPILER_BARRIER(); + __atomic_store_n(&a->_v, v, __ATOMIC_RELEASE); + V_COMPILER_BARRIER(); +} + +static inline vuint32_t vatomic32_cmpxchg_rel(vatomic32_t *a, vuint32_t e, vuint32_t v) +{ + vuint32_t exp = (vuint32_t)e; + V_COMPILER_BARRIER(); + __atomic_compare_exchange_n(&a->_v, &exp, (vuint32_t)v, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED); + V_COMPILER_BARRIER(); + return exp; +} + +static inline vuint32_t vatomic32_await_eq_acq(const vatomic32_t *a, vuint32_t c) +{ + vuint32_t ret = c; + vuint32_t o = 0; + await_while((o = vatomic32_read_acq(a)) != c) + { + vatomic_cpu_pause(); + ret = o; + } + return ret; +} \ No newline at end of file diff --git a/static_core/libvsync/include/vsync/queue/bounded_mpmc.h b/static_core/libvsync/include/vsync/queue/bounded_mpmc.h new file mode 100644 index 0000000000000000000000000000000000000000..7e03f25ba69db815455f324f090976412e06ef3d --- /dev/null +++ b/static_core/libvsync/include/vsync/queue/bounded_mpmc.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2025 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. + */ +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. + * SPDX-License-Identifier: MIT + */ + +#ifndef VQUEUE_BOUNDED_H +#define VQUEUE_BOUNDED_H +/******************************************************************************* + * @file bounded_mpmc.h + * @brief Lockless, multi-producer, multi-consumer bounded queue + * @ingroup linearizable + * + * The queue has a bounded size and returns errors in case the queue is full, + * empty, or if there is contention while trying to reserve an entry to enqueue + * or dequeue. + * + * @example + * @include eg_bounded_mpmc.c + * + * Typically, one tries to enqueue or dequeue as long the return value is + * different from QUEUE_BOUNDED_OK. + * + * The retrying loop can be combined with exponential backoff. + * + * @cite This is a variation of the algorithm in buf_ring.h of FreeBSD 8.0.0. + ******************************************************************************/ +#include +#ifndef ASSERT +#include +#define ASSERT(V) assert(V) +#endif +#include + +typedef struct bounded_mpmc_s { + vatomic32_t phead; + vatomic32_t ptail; + + vatomic32_t chead; + vatomic32_t ctail; + + void **buf; + vuint32_t size; +} bounded_mpmc_t; + +/** + * Initializes the given queue object. + * + * @param q address of bounded_mpmc_t object. + * @param b address of an array of void* slots with size `s`. + * @param s number of elements in array b (does not have to be power of 2). + */ +static inline void bounded_mpmc_init(bounded_mpmc_t *q, void **b, vuint32_t s) +{ + ASSERT(b && "buffer is NULL"); + ASSERT(s != 0 && "buffer with 0 size"); + + q->buf = b; + q->size = s; + vatomic32_init(&q->chead, 0); + vatomic32_init(&q->ctail, 0); + vatomic32_init(&q->phead, 0); + vatomic32_init(&q->ptail, 0); +} + +/** + * Tries to enqueue a value. + * + * @param q address of bounded_mpmc_t object. + * @param v address of object to enqueue. + * + * @return QUEUE_BOUNDED_OK if successful. + * @return QUEUE_BOUNDED_FULL if queue is full. + * @return QUEUE_BOUNDED_AGAIN if failed to enqueue, the caller should try + * again. + */ +static inline bounded_ret_t bounded_mpmc_enq(bounded_mpmc_t *q, void *v) +{ + vuint32_t curr, next; + + /* try to move producer head */ + curr = vatomic32_read_acq(&q->phead); + if (curr - vatomic32_read_rlx(&q->ctail) == q->size) { + return QUEUE_BOUNDED_FULL; + } + next = curr + 1; + if (vatomic32_cmpxchg_rel(&q->phead, curr, next) != curr) { + return QUEUE_BOUNDED_AGAIN; + } + /* push value into buffer */ + q->buf[curr % q->size] = v; + + /* mode producer tail */ + vatomic32_await_eq_acq(&q->ptail, curr); + vatomic32_write_rel(&q->ptail, next); + + return QUEUE_BOUNDED_OK; +} + +/** + * Tries to dequeue a value. + * + * @param q address of bounded_mpmc_t object. + * @param v output parameter of type (void**). Contains the address of the + * dequeued object, if the dequeue was successful. + * + * @return QUEUE_BOUNDED_OK if successful. + * @return QUEUE_BOUNDED_EMPTY if queue is empty. + * @return QUEUE_BOUNDED_AGAIN if failed to dequeue, the caller should try + * again. + */ +static inline bounded_ret_t bounded_mpmc_deq(bounded_mpmc_t *q, void **v) +{ + vuint32_t curr, next; + + /* try to move consumer head */ + curr = vatomic32_read_acq(&q->chead); + next = curr + 1; + if (curr == vatomic32_read_acq(&q->ptail)) { + return QUEUE_BOUNDED_EMPTY; + } + if (vatomic32_cmpxchg_rel(&q->chead, curr, next) != curr) { + return QUEUE_BOUNDED_AGAIN; + } + /* read value */ + *v = q->buf[curr % q->size]; + + /* move consumer tail */ + vatomic32_await_eq_rlx(&q->ctail, curr); + vatomic32_write_rel(&q->ctail, next); + + return QUEUE_BOUNDED_OK; +} + +#endif diff --git a/static_core/libvsync/include/vsync/queue/internal/bounded_ret.h b/static_core/libvsync/include/vsync/queue/internal/bounded_ret.h new file mode 100644 index 0000000000000000000000000000000000000000..88ebefbc7dcc9b0bc2f30828e0d39bb5236defe4 --- /dev/null +++ b/static_core/libvsync/include/vsync/queue/internal/bounded_ret.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 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. + */ +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. + * SPDX-License-Identifier: MIT + */ + +#ifndef VQUEUE_BOUNDED_RET_H +#define VQUEUE_BOUNDED_RET_H + +typedef enum { + QUEUE_BOUNDED_OK = 0, + QUEUE_BOUNDED_FULL = 1, + QUEUE_BOUNDED_EMPTY = 2, + QUEUE_BOUNDED_AGAIN = 3, +} bounded_ret_t; + +#endif diff --git a/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml b/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml index e49329f5e323d00fe2487bc1123e7351ac8d8e79..191175f6ec9ea5d6217e6c03debac0a142002e00 100644 --- a/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml +++ b/static_core/plugins/ets/runtime/ets_libbase_runtime.yaml @@ -5870,6 +5870,56 @@ intrinsics: args: [] impl: ark::ets::intrinsics::StdSystemAtomicFlagGet + - name: StdIndexQueuePush + space: ets + class_name: std.debug.concurrency.IndexQueue + method_name: push + static: false + signature: + ret: i32 + args: [i32] + impl: ark::ets::intrinsics::StdSystemIndexQueuePush + + - name: StdIndexQueuePop + space: ets + class_name: std.debug.concurrency.IndexQueue + method_name: pop + static: false + signature: + ret: i32 + args: [] + impl: ark::ets::intrinsics::StdSystemIndexQueuePop + + - name: StdIndexQueueSize + space: ets + class_name: std.debug.concurrency.IndexQueue + method_name: size + static: false + signature: + ret: i32 + args: [] + impl: ark::ets::intrinsics::StdSystemIndexQueueSize + + - name: StdIndexQueueInit + space: ets + class_name: std.debug.concurrency.IndexQueue + method_name: init + static: false + signature: + ret: i32 + args: [i32] + impl: ark::ets::intrinsics::StdSystemIndexQueueInit + + - name: StdIndexQueueFini + space: ets + class_name: std.debug.concurrency.IndexQueue + method_name: fini + static: false + signature: + ret: void + args: [] + impl: ark::ets::intrinsics::StdSystemIndexQueueFini + #################### # std.core.Runtime # #################### diff --git a/static_core/plugins/ets/runtime/intrinsics/std_core.cpp b/static_core/plugins/ets/runtime/intrinsics/std_core.cpp index a32b123ebecaccdc03f1a4059ad5abd6bea045db..022dada74b6ab38a3d52ca9b08e3173833f7a1e2 100644 --- a/static_core/plugins/ets/runtime/intrinsics/std_core.cpp +++ b/static_core/plugins/ets/runtime/intrinsics/std_core.cpp @@ -20,6 +20,7 @@ #include "plugins/ets/runtime/ets_panda_file_items.h" #include "plugins/ets/runtime/ets_platform_types.h" #include "plugins/ets/runtime/ets_vm.h" +#include "plugins/ets/runtime/types/ets_index_queue.h" #include "plugins/ets/runtime/types/ets_atomic_flag.h" #include "plugins/ets/runtime/types/ets_atomic_int.h" #include "plugins/ets/runtime/types/ets_class.h" @@ -394,4 +395,29 @@ extern "C" EtsBoolean StdSystemIsExternalTimerEnabled() return ark::ets::ToEtsBoolean(EtsCoroutine::GetCurrent()->GetManager()->IsExternalTimerEnabled()); } +extern "C" EtsInt StdSystemIndexQueuePush(EtsIndexQueue *instance, EtsInt v) +{ + return instance->Push(v); +} + +extern "C" EtsInt StdSystemIndexQueuePop(EtsIndexQueue *instance) +{ + return instance->Pop(); +} + +extern "C" EtsInt StdSystemIndexQueueSize(EtsIndexQueue *instance) +{ + return instance->Size(); +} + +extern "C" EtsInt StdSystemIndexQueueInit(EtsIndexQueue *instance, EtsInt capacity) +{ + return instance->Init(capacity); +} + +extern "C" void StdSystemIndexQueueFini(EtsIndexQueue *instance) +{ + instance->Fini(); +} + } // namespace ark::ets::intrinsics diff --git a/static_core/plugins/ets/runtime/intrinsics_declaration.h b/static_core/plugins/ets/runtime/intrinsics_declaration.h index 3c7409d2060e2666bb66d09874a66086b939b1de..05cdda237aba5f3dd4fa69f46879e613ac0bf178 100644 --- a/static_core/plugins/ets/runtime/intrinsics_declaration.h +++ b/static_core/plugins/ets/runtime/intrinsics_declaration.h @@ -18,6 +18,7 @@ #include "plugins/ets/runtime/types/ets_abc_file.h" #include "plugins/ets/runtime/types/ets_array.h" +#include "plugins/ets/runtime/types/ets_index_queue.h" #include "plugins/ets/runtime/types/ets_atomic_flag.h" #include "plugins/ets/runtime/types/ets_atomic_int.h" #include "plugins/ets/runtime/types/ets_escompat_array.h" diff --git a/static_core/plugins/ets/runtime/types/ets_index_queue.h b/static_core/plugins/ets/runtime/types/ets_index_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..44ff78b576bee27700875abaf6acfc2377cc455d --- /dev/null +++ b/static_core/plugins/ets/runtime/types/ets_index_queue.h @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2025 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. + */ + +#ifndef PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_INDEX_QUEUE_H +#define PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_INDEX_QUEUE_H + +#include "plugins/ets/runtime/types/ets_object.h" + +namespace ark::ets { + +namespace test { +class EtsIndexQueueTest; +} // namespace test + +namespace vsync { +extern "C" { +#include +} +} // namespace vsync + +class EtsIndexQueue : private ObjectHeader { + enum Error { + OK = 0, + EMPTY = -1, + FULL = -2, + AGAIN = -3, + INVALID = -4, + NOMEM = -100, + }; + +private: + vsync::bounded_mpmc_t bq_; + EtsInt dummy_; + +public: + EtsIndexQueue() = delete; + ~EtsIndexQueue() = delete; + + EtsInt Init(EtsInt capacity) + { + if (capacity <= 0) { + return INVALID; + } + + // bounded_mpmc assumes the underlying buffer contains items of the + // size of pointers. + void **buffer = new void *[capacity * sizeof(void *)]; + if (buffer == nullptr) { + return NOMEM; + } + + vsync::bounded_mpmc_init(&this->bq_, buffer, capacity); + return OK; + } + void Fini() + { + delete[] this->bq_.buf; + } + + EtsInt Push(EtsInt v) + { + if (v < 0) { + return INVALID; + } + + switch (vsync::bounded_mpmc_enq(&this->bq_, reinterpret_cast(v))) { + case vsync::QUEUE_BOUNDED_FULL: + return FULL; + case vsync::QUEUE_BOUNDED_AGAIN: + return AGAIN; + default: + return OK; + } + } + + EtsInt Pop() + { + void *ret; + switch (vsync::bounded_mpmc_deq(&this->bq_, &ret)) { + case vsync::QUEUE_BOUNDED_EMPTY: + return EMPTY; + case vsync::QUEUE_BOUNDED_AGAIN: + return AGAIN; + default: + return reinterpret_cast(ret); + } + } + + EtsInt Size() + { + std::uint32_t chead = vatomic32_read_rlx(&this->bq_.chead); + std::uint32_t ptail = vatomic32_read_rlx(&this->bq_.ptail); + EtsInt diff = ptail - chead; + return diff < 0 ? static_cast(AGAIN) : diff; + } + + NO_COPY_SEMANTIC(EtsIndexQueue); + NO_MOVE_SEMANTIC(EtsIndexQueue); + + static EtsIndexQueue *FromCoreType(ObjectHeader *bq) + { + return reinterpret_cast(bq); + } + + static const EtsIndexQueue *FromCoreType(const ObjectHeader *bq) + { + return reinterpret_cast(bq); + } + + ObjectHeader *GetCoreType() + { + return reinterpret_cast(this); + } + + const ObjectHeader *GetCoreType() const + { + return reinterpret_cast(this); + } + + EtsObject *AsObject() + { + return EtsObject::FromCoreType(this); + } + + const EtsObject *AsObject() const + { + return EtsObject::FromCoreType(this); + } + + static EtsIndexQueue *FromEtsObject(EtsObject *bq) + { + return reinterpret_cast(bq); + } + + static const EtsIndexQueue *FromEtsObject(const EtsObject *bq) + { + return reinterpret_cast(bq); + } + + friend class test::EtsIndexQueueTest; +}; + +} // namespace ark::ets + +#endif // PANDA_PLUGINS_ETS_RUNTIME_TYPES_ETS_INDEX_QUEUE_H diff --git a/static_core/plugins/ets/stdlib/std/containers/BoundedBlockingQueue.ets b/static_core/plugins/ets/stdlib/std/containers/BoundedBlockingQueue.ets new file mode 100644 index 0000000000000000000000000000000000000000..0834ff78164e439d280d5f316aac90b5c4282493 --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/containers/BoundedBlockingQueue.ets @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2025 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. + */ +package std.containers; + +export namespace containers { + /* + * Implementation of a bounded queue based on a bounded queue from libvsync. + */ + export class BoundedBlockingQueue implements BlockingQueue { + // BoundedBlockingQueue is implemented by three objects with the same queueCapacity. + // - array keeps the actual items of type T + // - freeQueue is initialized containing the indices 0..queueCapacity-1 and holds indices of array that are free. + // - usedQueue is initialized as empty and holds indices of array that have been written to. + // + // When a coroutine wants to enqueue an item (see push()), it pops an index i from freeQueue, uses that index to + // access the array in isolation, ie, the coroutine owns index i. Once the coroutine wrote to array[i], it + // releases the ownership of the index by pushing index i into the usedQueue. To pop an item, the coroutine first + // tries to get a used index i from usedQueue and upon completion pushs index i into freeQueue. + // + private queueCapacity: int; + private freeQueue: IndexQueue; + private usedQueue: IndexQueue; + private array: Array; + + /** + * Constructs a BoundedBlockingQueue with provided capaity. + * + * @param { int } capacity the capacity of the BoundedBlockingQueue + */ + constructor(capacity: int = 1000) { + this.freeQueue = new IndexQueue(capacity) + this.usedQueue = new IndexQueue(capacity) + this.array = new Array(capacity) + this.queueCapacity = capacity + for (let i=0; i= 0) or EMPTY. + return i + } + } + + // Pops an index from q. Blocks until a index is available. + private popIndex(q: IndexQueue): int { + const MAX_SPINS = 100 + let count = 0 + + while (true) { + let i = this.tryPopIndex(q) + if (i >= 0) { + return i + } else if (i != IndexQueueStatus.EMPTY) { + throw Error("Unexpected indexQueue status on pop") + } + // retries up to MAX_SPINS times before yielding the coroutine. + if (count < MAX_SPINS) { + count++ + continue + } + Coroutine.Schedule() + } + } + + // Pushes index into q. Non-blocking by construction. + private pushIndex(q: IndexQueue, v: int): void { + while (true) { + let i = q.push(v) + // By construction, there are at most queueCapacity indices in the BoundedBlockingQueue. Both indexQueues + // have enough capacity to fit all these indices. Therefore, they never return FULL. + if (i == IndexQueueStatus.OK) { + return + } else if (i == IndexQueueStatus.AGAIN) { + // The only possible status returned by a indexQueue is AGAIN when the atomic operations of two + // parallel coroutines conflicted. In that case it is safe and faster to retry without yielding the + // coroutine. + continue + } else { + throw Error("Unexpected indexQueue status on push") + } + } + } + + override push(element: T): void { + let i = this.popIndex(this.freeQueue) + this.array[i] = element + this.pushIndex(this.usedQueue, i) + } + + override pop(): T { + let i = this.popIndex(this.usedQueue) + let popped = this.array[i] + this.array[i] = undefined + this.pushIndex(this.freeQueue, i) + return popped as T + } + + override add(element: T): boolean { + let i = this.tryPopIndex(this.freeQueue) + if (i == IndexQueueStatus.FULL) { + return false + } + this.array[i] = element + this.pushIndex(this.usedQueue, i) + return true + } + + override poll(): T | undefined { + let i = this.tryPopIndex(this.usedQueue) + if (i == IndexQueueStatus.EMPTY) { + return undefined + } + let popped = this.array[i] + this.array[i] = undefined + this.pushIndex(this.freeQueue, i) + return popped as T + } + + override getFirst(): T | undefined { + throw Error(`This method is not supported by ${Type.of(this)}`) + } + + override isEmpty(): boolean { + return this.size == 0 + } + + override remainingCapacity(): int { + return this.queueCapacity - this.size + } + + destruct(): void { + this.freeQueue.fini() + this.usedQueue.fini() + } + + override get size(): int { + let n = this.usedQueue.size() + if (n >= 0) { + return n + } else { + throw Error("Cannot get the size of the used queue.") + } + } + + override get capacity(): int { + return this.queueCapacity + } + } +} diff --git a/static_core/plugins/ets/stdlib/std/debug/concurrency/IndexQueue.ets b/static_core/plugins/ets/stdlib/std/debug/concurrency/IndexQueue.ets new file mode 100644 index 0000000000000000000000000000000000000000..776ca37088c36bc76c0033865836d14b9f954e94 --- /dev/null +++ b/static_core/plugins/ets/stdlib/std/debug/concurrency/IndexQueue.ets @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 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. + */ + +package std.debug.concurrency; + +export enum IndexQueueStatus { + // operation successful + OK = 0, + // queue is empty + EMPTY = -1, + // queue is full + FULL = -2, + // a concurrency conflict occurred, user should try again + AGAIN = -3, + // an invalid index (negative) was given to push + INVALID = -4 +} + +export final class IndexQueue { + + // The following opaque fields should match the bytes occupied by IndexQueue in C++ + // Order of fields should be the following + // 1. Reference fields + // 2. Primitive fields in mostly size decreasing order filling alignment holes + private opaque0 : long; + private opaque1 : long; + private opaque2 : long; + private opaque3 : long; + private opaque4 : int; + + // returns IndexQueueStatus: OK, FULL, AGAIN, INVALID + public native push(v: int): int; + + // returns positive value or IndexQueueStatus: EMPTY, AGAIN + public native pop(): int; + + // returns positive value or IndexQueueStatus: AGAIN + public native size(): int; + + constructor(cap: int) { + let ok = this.init(cap) + + if (ok == -100) { + // init fails with -100 if no memory is available + throw Error("could not allocate IndexQueue"); + } else if (ok == IndexQueueStatus.INVALID) { + throw Error("invalid IndexQueue capacity"); + } + } + private native init(cap: int): int; + public native fini(): void; +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_AddTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_AddTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..bf3798a1483fea77752601af09f5c66c6d4f2269 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_AddTest.ets @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2025 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. + */ + +import { launch } from "std/concurrency" + +const elementsNum: int = 100; +let b1: int | null = 0; +let f1: int | null = 0; + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueueAddTest"); + + BoundedBlockingQueueTestsuite.addTest("AddTestInt001", () => + { + let queueIntAdd: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i: int = 0; i < elementsNum; ++i) { + queueIntAdd.add(i); + } + arktest.assertEQ(queueIntAdd.size, elementsNum); + queueIntAdd.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("AddTestStr002", () => + { + let queueStrAdd: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i: int = 0; i < elementsNum; ++i) { + queueStrAdd.add("test"); + } + queueStrAdd.add("teststring"); + arktest.assertEQ(queueStrAdd.size, elementsNum+1); + queueStrAdd.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("AddTestInt006", () => + { + let queueIntPopN: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + while (!queueIntPopN.isEmpty()) { + queueIntPopN.pop(); + } + let p1 = launch) => void>(queueIntAdddata, queueIntPopN); + let p2 = launch) => void>(queueIntAdddata, queueIntPopN); + let p3 = launch) => void>(queueIntAdddata, queueIntPopN); + let p4 = launch) => void>(queueIntAdddata, queueIntPopN); + p1.Await(); + p2.Await(); + p3.Await(); + p4.Await(); + arktest.assertFalse(queueIntPopN.isEmpty()); + + queueIntPopN.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("AddTestInt007", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i: int = 0; i < elementsNum; ++i) { + queueInt.add(i); + } + arktest.assertEQ(queueInt.size, elementsNum); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("AddTestInt008", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + while (!queueInt.isEmpty()) { + queueInt.pop(); + } + for (let i: int = 0; i < elementsNum; ++i) { + queueInt.add(i); + } + arktest.assertFalse(queueInt.isEmpty()); + queueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} + +function queueIntAdddata(queueIntPopN: containers.BoundedBlockingQueue) { + queueIntPopN.add(1); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_CapacityTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_CapacityTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..6002abed08e6404a4389facded8a9f4cdff08e43 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_CapacityTest.ets @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2025 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. + */ + +const elementsNum: int = 10; +const testNum: int = 20; +const stressNum: int = 100; + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueueCapacityTest"); + + BoundedBlockingQueueTestsuite.addTest("CapacityTest001", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(elementsNum); + for (let i = 0; i < 5; i++) { + arktest.assertEQ(queueInt.add(i), true); + } + arktest.assertEQ(queueInt.isEmpty(), false); + arktest.assertEQ(queueInt.size, 5); + arktest.assertEQ(queueInt.capacity, elementsNum); + arktest.assertEQ(queueInt.add(testNum), true); + arktest.assertEQ(queueInt.size, 6); + arktest.assertEQ(queueInt.capacity, elementsNum); + + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("CapacityTest007", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(stressNum); + for (let i = 0; i < 10; i++) { + arktest.assertEQ(queueInt.capacity, stressNum); + queueInt.add(i); + } + for (let i = 10; i < 20; i++) { + queueInt.push(i); + } + arktest.assertEQ(queueInt.capacity, stressNum); + + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("GetCapacityStressTest008", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(stressNum); + for (let i = 0; i < stressNum; i++) { + queueInt.add(i); + arktest.assertEQ(queueInt.capacity, stressNum); + } + arktest.assertEQ(queueInt.size, stressNum); + queueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_ConstructorTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_ConstructorTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..8bd3c1a00e6d65a13b09cb5e1256bdc772c18aae --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_ConstructorTest.ets @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025 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. + */ + +let flag: int = 0; +const arr: Number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +function main() { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueueConstructorTest"); + + BoundedBlockingQueueTestsuite.addTest("ConstructorTestInt001", () => + { + let queueIntA: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + arktest.assertEQ(queueIntA.isEmpty(), true); + arktest.assertEQ(queueIntA.size, 0); + queueIntA.destruct(); + }); + BoundedBlockingQueueTestsuite.run(); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_GetFirstTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_GetFirstTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..9207497894096a2f89e57270d874811d88a66659 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_GetFirstTest.ets @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2025 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. + */ + +import { launch } from "std/concurrency" + +const elementsNum: int = 10; +const stressNum: int = 100; +let b1: int | null = 0; +let f1: int | null = 0; +let f2: int | null = 0; +let backNum: int | null = 0; + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueueGetFirstTest"); + + BoundedBlockingQueueTestsuite.addTest("GetFirstTest001", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i = 0; i < elementsNum; i++) { + arktest.assertEQ(queueInt.add(i), true); + } + try{ + let res = queueInt.getFirst(); + // Test fails + arktest.assertTrue(false); + } + catch (e){ + arktest.assertTrue(e instanceof Error); + + } + queueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_GetSizeTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_GetSizeTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..0d3d95470256f5486fdd79b550d8d70e79b6ba3f --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_GetSizeTest.ets @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2025 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. + */ + +import { launch } from "std/concurrency" + +const elementsNum: int = 100; +let sizeNum: int = 0; + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueueGetSizeTest"); + + BoundedBlockingQueueTestsuite.addTest("GetSizeTestInt003", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + while (!queueInt.isEmpty()) { + queueInt.pop(); + } + for (let i: int = 0; i < elementsNum; ++i) { + queueInt.push(i); + } + let p1 = launch) => void>(queueIntSizedata, queueInt); + let p2 = launch) => void>(queueIntSizedata, queueInt); + let p3 = launch) => void>(queueIntSizedata, queueInt); + let p4 = launch) => void>(queueIntSizedata, queueInt); + p1.Await(); + p2.Await(); + p3.Await(); + p4.Await(); + arktest.assertEQ(queueInt.size, elementsNum); + queueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} + +function queueIntSizedata(queueInt:containers.BoundedBlockingQueue) { + sizeNum = queueInt.size; +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PollTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PollTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..273c4389c823351df0b4ea87c1a376ede77eb763 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PollTest.ets @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2025 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. + */ + +import { launch } from "std/concurrency" +import { Job } from "std/core" + +const elementsNum: int = 10; +const stressNum: int = 100; + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueuePollTest"); + + BoundedBlockingQueueTestsuite.addTest("PollTest001", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i = 0; i < elementsNum; i++) { + arktest.assertEQ(queueInt.add(i), true); + } + arktest.assertEQ(queueInt.size, elementsNum); + for (let i = 0; i < 5; i++) { + arktest.assertEQ(queueInt.poll(), i); + } + arktest.assertEQ(queueInt.size, 5); + for (let i = 5; i < 10; i++) { + arktest.assertEQ(queueInt.poll(), i); + } + arktest.assertEQ(queueInt.size, 0); + arktest.assertEQ(queueInt.poll(), undefined); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("PollStressTest004", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(stressNum); + for (let i = 0; i < stressNum; i++) { + queueInt.add(i); + } + for (let i = 0; i < stressNum; i++) { + arktest.assertEQ(queueInt.poll(), i); + } + arktest.assertEQ(queueInt.size, 0); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("ThreadsPollStressTest003", () => + { + let sMTQueueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(stressNum); + while (!sMTQueueInt.isEmpty()) { + sMTQueueInt.pop(); + } + for (let i = 0; i < stressNum; i++) { + sMTQueueInt.add(i); + } + let jobs = new Array>(100); + let p = 0; + for (let i = 0; i < stressNum; i++) { + jobs[p++] = launch) => void>(sQueuePollData, sMTQueueInt); + } + for (let i: int = 0; i < stressNum; i++) { + jobs[i].Await(); + } + arktest.assertEQ(sMTQueueInt.size, 0); + sMTQueueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} + +function sQueuePollData(sMTQueueInt: containers.BoundedBlockingQueue) { + sMTQueueInt.poll(); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PopTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PopTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..b60ead9788f57d2eddbf4ac1f961d335c2ad4a00 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PopTest.ets @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2025 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. + */ + +import { launch } from "std/concurrency" +import { Job } from "std/core" + +const elementsNum: int = 100; + +let testArrayStr: Array = new Array(elementsNum); + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueuePopTest"); + + BoundedBlockingQueueTestsuite.addTest("ConcurrentPushPopTestStr", () => + { + let queueStr: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + let p1 = launch) => void>(pusherStr, queueStr); + let arrayToCheck = popperStr(queueStr); + p1.Await(); + for (let i = 0; i < elementsNum; i++) { + arktest.assertEQ(testArrayStr[i], arrayToCheck[i]); + } + arktest.assertEQ(queueStr.size, 0); + queueStr.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("PopTestInt001", () => + { + let queueIntPop1: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + arktest.assertEQ(queueIntPop1.size, 0); + for (let i: int = 0; i < elementsNum; ++i) { + queueIntPop1.push(i); + } + arktest.assertEQ(queueIntPop1.pop(), 0); + arktest.assertEQ(queueIntPop1.size, elementsNum - 1); + arktest.assertTrue(queueIntPop1.pop() != 0); + queueIntPop1.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("PopTestInt002", () => + { + let queueIntPop2: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i: int = 0; i < elementsNum; ++i) { + queueIntPop2.push(i); + } + + queueIntPop2.pop() + arktest.assertEQ(queueIntPop2.size, elementsNum - 1); + while (!queueIntPop2.isEmpty()) { + queueIntPop2.pop(); + } + let p1 = launch) => void>(BoundedBlockingQueuePop, queueIntPop2); + let i = queueIntPop2.pop(); + p1.Await(); + arktest.assertEQ(i, 10); + queueIntPop2.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("PopTestInt003", () => + { + let queueIntPopN: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + let p1 = launch) => void>(queueIntPopNdata, queueIntPopN); + for (let i: int = 0; i < elementsNum; ++i) { + queueIntPopN.push(i); + } + p1.Await(); + arktest.assertEQ(queueIntPopN.size, 0); + queueIntPopN.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("PopTestInt004", () => + { + let queueIntPop: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + arktest.assertEQ(queueIntPop.size, 0); + for (let i: int = 0; i < elementsNum; ++i) { + let el = Double.toInt(random() * 10000); + queueIntPop.push(el); + } + arktest.assertEQ(queueIntPop.size, elementsNum); + let j: int = 1; + for (let i = 0; i < elementsNum; i++) { + queueIntPop.pop(); + arktest.assertEQ(queueIntPop.size, elementsNum - j); + j++; + } + queueIntPop.destruct(); + }); + + BoundedBlockingQueueTestsuite.addTest("PopTestInt005", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + const asyncTestnum: int = 50; + let jobs = new Array>(100); + for (let i: int = 0; i < asyncTestnum; ++i) { + jobs[i] = launch) => void>(queueIntPopdata, queueInt); + } + for (let i: int = 0; i < asyncTestnum; ++i) { + queueInt.push(i); + } + for (let i: int = 0; i < asyncTestnum; i++) { + jobs[i].Await(); + } + arktest.assertEQ(queueInt.size, 0); + queueInt.destruct(); + }); + + BoundedBlockingQueueTestsuite.addTest("PopTestInt006", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + for (let i: int = 0; i < elementsNum; ++i) { + queueInt.push(i); + } + let p2 = launch) => void>(queueIntPopdata, queueInt); + + p2.Await(); + arktest.assertEQ(queueInt.size, elementsNum - 1); + queueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} + +function pusherStr(queueStr: containers.BoundedBlockingQueue) { + for (let i = 0; i < elementsNum; i++) { + if (i == 0) { + let el = String("abacaba"); + testArrayStr[i] = el; + queueStr.push(el); + } else { + let el = testArrayStr[i - 1] + "aba"; + testArrayStr[i] = el; + queueStr.push(el); + } + } +} + +function popperStr(queueStr: containers.BoundedBlockingQueue): Array { + let arr = new Array(elementsNum); + for (let i = 0; i < elementsNum; i++) { + arr[i] = queueStr.pop(); + } + return arr; +} + +function queueIntPopdata(q: containers.BoundedBlockingQueue) { + q.pop(); +} + +function BoundedBlockingQueuePop (queueIntPop2: containers.BoundedBlockingQueue) { + queueIntPop2.push(10); +} + +function queueIntPopNdata (q: containers.BoundedBlockingQueue) { + for (let i: int = 0; i < elementsNum; ++i) { + q.pop(); + } +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PushTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PushTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..739d09051efaad07bd7a767b776a9b731d16dedb --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_PushTest.ets @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2025 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. + */ + +import { launch } from "std/concurrency" + +const elementsNum: int = 100; + +let testArrayInt: Array = new Array(elementsNum); +let testArrayObj: Array = new Array(elementsNum); +let testArrayStr: Array = new Array(elementsNum); + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueuePushTest"); + + BoundedBlockingQueueTestsuite.addTest("SimpleQueuePushPopFrontBackSizeEmptyTest", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + let arr: Array = new Array(elementsNum); + for (let i: int = 0; i < arr.length; ++i) { + let el = Double.toInt(random() * 10000); + arr[i] = el; + queueInt.push(el); + } + let ind: int = 0; + while (!queueInt.isEmpty()) { + arktest.assertEQ(queueInt.pop(), arr[ind++]); + } + arktest.assertEQ(ind, arr.length); + arktest.assertEQ(queueInt.size, 0); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("ConcurrentPushPopTestInt", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + let p1 = launch) => void>(pusherInt, queueInt); + let arrayToCheck = popperInt(queueInt); + for (let i = 0; i < elementsNum; i++) { + arktest.assertEQ(testArrayInt[i], arrayToCheck[i]); + } + p1.Await(); + arktest.assertEQ(queueInt.size, 0); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("ConcurrentPushPopTestObj", () => + { + let queueObj: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + let p1 = launch) => void>(pusherObj, queueObj); + let arrayToCheck = popperObj(queueObj); + for (let i = 0; i < elementsNum; i++) { + arktest.assertEQ(testArrayObj[i], arrayToCheck[i]); + } + p1.Await(); + arktest.assertEQ(queueObj.size, 0); + queueObj.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("ConcurrentPushPopTestStr", () => + { + let queueStr: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(200); + let p1 = launch) => void>(pusherStr, queueStr); + let arrayToCheck = popperStr(queueStr); + for (let i = 0; i < elementsNum; i++) { + arktest.assertEQ(testArrayStr[i], arrayToCheck[i]); + } + p1.Await(); + arktest.assertEQ(queueStr.size, 0); + queueStr.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); + +} + +function pusherInt(queueInt: containers.BoundedBlockingQueue) { + for (let i = 0; i < elementsNum; i++) { + let el = Double.toInt(random() * 10000); + testArrayInt[i] = el; + queueInt.push(el); + } +} + +function pusherObj(queueObj: containers.BoundedBlockingQueue) { + for (let i = 0; i < elementsNum; i++) { + let el = new Object(); + testArrayObj[i] = el; + queueObj.push(el); + } +} + +function pusherStr(queueStr: containers.BoundedBlockingQueue) { + for (let i = 0; i < elementsNum; i++) { + if (i == 0) { + let el = String("abacaba"); + testArrayStr[i] = el; + queueStr.push(el); + } else { + let el = testArrayStr[i - 1] + "aba"; + testArrayStr[i] = el; + queueStr.push(el); + } + } +} + +function popperInt(queueInt: containers.BoundedBlockingQueue): Array { + let arr = new Array(elementsNum); + for (let i = 0; i < elementsNum; i++) { + arr[i] = queueInt.pop(); + } + return arr; +} + +function popperObj(queueObj: containers.BoundedBlockingQueue): Array { + let arr = new Array(elementsNum); + for (let i = 0; i < elementsNum; i++) { + arr[i] = queueObj.pop(); + } + return arr; +} + +function popperStr(queueStr: containers.BoundedBlockingQueue): Array { + let arr = new Array(elementsNum); + for (let i = 0; i < elementsNum; i++) { + arr[i] = queueStr.pop(); + } + return arr; +} + +function queueIntFrontdata(queueInt: containers.BoundedBlockingQueue) { + queueInt.getFirst(); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_RemainingCapacityTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_RemainingCapacityTest.ets new file mode 100755 index 0000000000000000000000000000000000000000..84a24e07f653dcb11f22af22cb46c15657eeb518 --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/containers/bounded_blocking_queue/BoundedBlockingQueue_RemainingCapacityTest.ets @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2025 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. + */ + +const elementsNum: int = 10; +const testNum: int = 20; +const stressNum: int = 100; + +function main(): int { + let BoundedBlockingQueueTestsuite = new arktest.ArkTestsuite("BoundedBlockingQueueRemainingCapacityTest"); + + BoundedBlockingQueueTestsuite.addTest("RemainingCapacityTest001", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(elementsNum); + for (let i = 0; i < 5; i++) { + arktest.assertEQ(queueInt.add(i), true); + } + arktest.assertEQ(queueInt.isEmpty(), false); + arktest.assertEQ(queueInt.size, 5); + arktest.assertEQ(queueInt.remainingCapacity(), 5); + arktest.assertEQ(queueInt.add(testNum), true); + arktest.assertEQ(queueInt.size, 6); + arktest.assertEQ(queueInt.remainingCapacity(), 4); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("RemainingCapacityTest006", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(stressNum); + for (let i = 0; i < 10; i++) { + arktest.assertEQ(queueInt.remainingCapacity(), stressNum - i); + queueInt.add(i); + } + for (let i = 10; i < 20; i++) { + queueInt.push(i); + } + arktest.assertEQ(queueInt.remainingCapacity(), stressNum - 20); + queueInt.destruct(); + }); + BoundedBlockingQueueTestsuite.addTest("RemainingCapacityStressTest009", () => + { + let queueInt: containers.BoundedBlockingQueue = new containers.BoundedBlockingQueue(stressNum); + let j: int = stressNum; + for (let i = 0; i < stressNum; i++) { + queueInt.add(i); + j--; + arktest.assertEQ(queueInt.remainingCapacity(), j); + } + arktest.assertEQ(queueInt.size, stressNum); + queueInt.destruct(); + }); + + return BoundedBlockingQueueTestsuite.run(); +} diff --git a/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt b/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt index 346c44f691858fbbcfed35194a05b9c555f05e97..27b811f88b4bd5ee459016925bc4e5f53dc64a10 100644 --- a/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt +++ b/static_core/plugins/ets/tests/runtime/types/CMakeLists.txt @@ -41,6 +41,7 @@ panda_ets_add_gtest( NO_CORES NAME ets_internal_objects_tests SOURCES + ets_index_queue_test.cpp ets_promise_test.cpp ets_job_test.cpp ets_class_test.cpp diff --git a/static_core/plugins/ets/tests/runtime/types/ets_index_queue_test.cpp b/static_core/plugins/ets/tests/runtime/types/ets_index_queue_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb22043413606a0acedc3779a99c178942639060 --- /dev/null +++ b/static_core/plugins/ets/tests/runtime/types/ets_index_queue_test.cpp @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2021-2025 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. + */ + +#include "ets_mirror_class_test_base.h" +//#include + +#include "types/ets_class.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-private-field" +#include "types/ets_index_queue.h" +#pragma GCC diagnostic pop +#include "tests/runtime/types/ets_test_mirror_classes.h" + +namespace ark::ets::test { + +class EtsIndexQueueTest : public EtsMirrorClassTestBase { +public: + static std::vector GetMembers() + { + return std::vector {MIRROR_FIELD_INFO(EtsIndexQueue, dummy_, "opaque4")}; + } +}; + +// Check both EtsIndexQueue and ark::Class has the same number of fields +// and at the same offsets +TEST_F(EtsIndexQueueTest, IndexQueueMemoryLayout) +{ + auto *klass = GetClassLinker()->GetClass("Lstd/debug/concurrency/IndexQueue;"); + MirrorFieldInfo::CompareMemberOffsets(klass, GetMembers(), false); +} + +} // namespace ark::ets::test diff --git a/static_core/scripts/clang-tidy/clang_tidy_check.py b/static_core/scripts/clang-tidy/clang_tidy_check.py index 79d53bcad07555cdf224ca4ab381530b57ec6ce6..2c4030c1b2d3d7594da89f2ba88c875bf3d33831 100755 --- a/static_core/scripts/clang-tidy/clang_tidy_check.py +++ b/static_core/scripts/clang-tidy/clang_tidy_check.py @@ -314,6 +314,10 @@ def need_to_ignore_file(file_path: str, panda_dir: str, build_dir: str) -> bool: if regexp.search(file_path): return True + regexp = re.compile(".*/libvsync/.*") + if regexp.search(file_path): + return True + return False diff --git a/static_core/scripts/code_style/doxygen_style_check.py b/static_core/scripts/code_style/doxygen_style_check.py index 358d12dcdcbc59a548ae35b8f86470811f088591..88798560507972f73680cc655ffd3be636fce3b8 100755 --- a/static_core/scripts/code_style/doxygen_style_check.py +++ b/static_core/scripts/code_style/doxygen_style_check.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright (c) 2023-2024 Huawei Device Co., Ltd. +# Copyright (c) 2025 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 @@ -31,7 +31,8 @@ def get_args(): def get_file_list(panda_dir) -> list: src_exts = (".c", '.cc', ".cp", ".cxx", ".cpp", ".CPP", ".c++", ".C", ".h", ".hh", ".H", ".hp", ".hxx", ".hpp", ".HPP", ".h++", ".tcc") - skip_dirs = ["third_party", "artifacts", "\..*", "build.*"] + # Skipping libvsync as it will eventually be added to third_party + skip_dirs = ["third_party", "artifacts", "\..*", "build.*", "libvsync"] file_list = [] for dirpath, dirnames, filenames in os.walk(panda_dir): dirnames[:] = [d for d in dirnames if not re.match(f"({')|('.join(skip_dirs)})", d)] @@ -174,6 +175,8 @@ def check_all(src_path: str, fine_patterns_found: list, wrong_patterns_number: i passed &= check_javadoc(src_path, fine_patterns_found[0]) passed &= check_additional_slashes(src_path, fine_patterns_found[1]) passed &= check_less_than_slashes(src_path, fine_patterns_found[2]) + if not passed: + print("Not passed -- " + src_path) return passed