1 /**
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "plugins/ets/runtime/finalreg/finalization_registry_manager.h"
17 #include "plugins/ets/runtime/ets_class_linker_context.h"
18 #include "plugins/ets/runtime/types/ets_array.h"
19 #include "plugins/ets/runtime/types/ets_method.h"
20
21 namespace ark::ets {
22
SortInstancies()23 void FinalizationRegistryManager::SortInstancies()
24 {
25 auto *objArray = EtsObjectArray::FromCoreType(vm_->GetGlobalObjectStorage()->Get(finRegInstancies_));
26 ASSERT(objArray != nullptr);
27 size_t head = 0;
28 size_t localIndex = objArray->GetLength();
29 size_t tail = localIndex - 1;
30 while (head < tail) {
31 while (head < tail && objArray->Get(head) != nullptr) {
32 head++;
33 }
34 while (head < tail && objArray->Get(tail) == nullptr) {
35 tail--;
36 }
37 if (head < tail) {
38 objArray->Set(head, objArray->Get(tail));
39 objArray->Set(tail, nullptr);
40 localIndex = tail;
41 }
42 }
43 finRegLastIndex_ = localIndex;
44 }
45
EnsureCapacity(EtsCoroutine * coro)46 void FinalizationRegistryManager::EnsureCapacity(EtsCoroutine *coro)
47 {
48 EtsClass *objectClass = vm_->GetClassLinker()->GetClassRoot(EtsClassRoot::OBJECT);
49 if (finRegInstancies_ == nullptr) {
50 auto *objArray = EtsObjectArray::Create(objectClass, INIT_SIZE, SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT);
51 ASSERT(objArray != nullptr);
52 finRegInstancies_ =
53 vm_->GetGlobalObjectStorage()->Add(objArray->GetCoreType(), ark::mem::Reference::ObjectType::GLOBAL);
54 finRegLastIndex_ = 0;
55 }
56 auto *objArray = EtsObjectArray::FromCoreType(vm_->GetGlobalObjectStorage()->Get(finRegInstancies_));
57 EtsHandle<EtsObjectArray> objArrayHandle(coro, objArray);
58 ASSERT(objArrayHandle.GetPtr() != nullptr);
59 size_t finRegCapacity = objArrayHandle->GetLength();
60 if (finRegLastIndex_ >= finRegCapacity) {
61 SortInstancies();
62 if (finRegLastIndex_ >= finRegCapacity) {
63 auto *newFinalizationRegistryInstances = EtsObjectArray::Create(objectClass, (finRegCapacity * 2U) + 1U,
64 SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT);
65 objArrayHandle->CopyDataTo(newFinalizationRegistryInstances);
66 vm_->GetGlobalObjectStorage()->Remove(finRegInstancies_);
67 finRegInstancies_ = vm_->GetGlobalObjectStorage()->Add(newFinalizationRegistryInstances->GetCoreType(),
68 ark::mem::Reference::ObjectType::GLOBAL);
69 }
70 }
71 }
72
RegisterInstance(EtsObject * instance)73 void FinalizationRegistryManager::RegisterInstance(EtsObject *instance)
74 {
75 ASSERT_MANAGED_CODE();
76 EtsCoroutine *coroutine = EtsCoroutine::GetCurrent();
77 [[maybe_unused]] EtsHandleScope scope(coroutine);
78 EtsHandle<EtsObject> instanceHandle(coroutine, instance);
79 EnsureCapacity(coroutine);
80 auto *objArray = EtsObjectArray::FromCoreType(vm_->GetGlobalObjectStorage()->Get(finRegInstancies_));
81 ASSERT(objArray != nullptr);
82 ASSERT(objArray->GetLength() != 0);
83 objArray->Set(finRegLastIndex_, instanceHandle.GetPtr());
84 finRegLastIndex_++;
85 }
86
CleanupCoroFinished()87 void FinalizationRegistryManager::CleanupCoroFinished()
88 {
89 // Atomic with acq_rel order reason: other threads should see correct value
90 [[maybe_unused]] uint32_t oldCnt = finRegCleanupCoroCount_.fetch_sub(1, std::memory_order_acq_rel);
91 ASSERT(oldCnt > 0);
92 }
93
UpdateFinRegCoroCountAndCheckIfCleanupNeeded()94 bool FinalizationRegistryManager::UpdateFinRegCoroCountAndCheckIfCleanupNeeded()
95 {
96 // Atomic with acquire order reason: getting correct value
97 uint32_t cnt = finRegCleanupCoroCount_.load(std::memory_order_acquire);
98 uint32_t oldCnt = cnt;
99 // Atomic with acq_rel order reason: sync for counter
100 while (cnt < MAX_FINREG_CLEANUP_COROS && !finRegCleanupCoroCount_.compare_exchange_weak(
101 // CC-OFFNXT(G.FMT.06-CPP) project code style
102 cnt, cnt + 1U, std::memory_order_acq_rel, std::memory_order_acquire)) {
103 oldCnt = cnt;
104 }
105 return oldCnt < MAX_FINREG_CLEANUP_COROS;
106 }
107
StartCleanupCoroIfNeeded(EtsCoroutine * coro)108 void FinalizationRegistryManager::StartCleanupCoroIfNeeded(EtsCoroutine *coro)
109 {
110 ASSERT(coro != nullptr);
111 auto *coroManager = coro->GetCoroutineManager();
112
113 if (finRegLastIndex_ != 0 && UpdateFinRegCoroCountAndCheckIfCleanupNeeded()) {
114 auto *objArray = EtsObjectArray::FromCoreType(vm_->GetGlobalObjectStorage()->Get(finRegInstancies_));
115 auto *event = Runtime::GetCurrent()->GetInternalAllocator()->New<CompletionEvent>(nullptr, coroManager);
116 Method *cleanup = PlatformTypes(vm_)->coreFinalizationRegistryExecCleanup->GetPandaMethod();
117 auto launchMode =
118 coroManager->IsMainWorker(coro) ? CoroutineLaunchMode::MAIN_WORKER : CoroutineLaunchMode::DEFAULT;
119 auto args = PandaVector<Value> {Value(objArray->GetCoreType()), Value(static_cast<uint32_t>(launchMode))};
120 [[maybe_unused]] bool launchResult = coroManager->Launch(event, cleanup, std::move(args), launchMode,
121 CoroutinePriority::DEFAULT_PRIORITY, false);
122 ASSERT(launchResult);
123 }
124 }
125
126 } // namespace ark::ets
127