1 /**
2 * Copyright (c) 2023-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 "libpandabase/os/mutex.h"
17 #include "runtime/include/object_header.h"
18 #include "runtime/mem/gc/gc_phase.h"
19 #include "runtime/mem/object_helpers.h"
20 #include "plugins/ets/runtime/mem/ets_reference_processor.h"
21 #include "plugins/ets/runtime/types/ets_class.h"
22 #include "plugins/ets/runtime/types/ets_weak_reference.h"
23 #include "plugins/ets/runtime/types/ets_finalizable_weak_ref.h"
24 #include "plugins/ets/runtime/ets_vm.h"
25
26 namespace ark::mem::ets {
27
EtsReferenceProcessor(GC * gc)28 EtsReferenceProcessor::EtsReferenceProcessor(GC *gc) : gc_(gc) {}
29
Initialize()30 void EtsReferenceProcessor::Initialize()
31 {
32 auto *vm = ark::ets::PandaEtsVM::GetCurrent();
33 ASSERT(vm != nullptr);
34 nullValue_ = ark::ets::EtsObject::FromCoreType(vm->GetNullValue());
35 ASSERT(gc_->GetObjectAllocator()->IsObjectInNonMovableSpace(nullValue_->GetCoreType()));
36 }
37
IsReference(const BaseClass * baseCls,const ObjectHeader * ref,const ReferenceCheckPredicateT & pred) const38 bool EtsReferenceProcessor::IsReference(const BaseClass *baseCls, const ObjectHeader *ref,
39 const ReferenceCheckPredicateT &pred) const
40 {
41 ASSERT(baseCls != nullptr);
42 ASSERT(ref != nullptr);
43 ASSERT(baseCls->GetSourceLang() == panda_file::SourceLang::ETS);
44
45 const auto *objEtsClass = ark::ets::EtsClass::FromRuntimeClass(static_cast<const Class *>(baseCls));
46
47 if (!objEtsClass->IsReference()) {
48 return false;
49 }
50 ASSERT(objEtsClass->IsWeakReference() || objEtsClass->IsFinalizerReference());
51
52 const auto *etsRef = reinterpret_cast<const ark::ets::EtsWeakReference *>(ref);
53
54 auto *referent = etsRef->GetReferent();
55 if (referent == nullptr || referent == nullValue_) {
56 LOG(DEBUG, REF_PROC) << "Treat " << GetDebugInfoAboutObject(ref)
57 << " as normal object, because referent is nullish";
58 return false;
59 }
60
61 if (!pred(referent->GetCoreType())) {
62 LOG(DEBUG, REF_PROC) << "Treat " << GetDebugInfoAboutObject(ref) << " as normal object, because referent "
63 << std::hex << GetDebugInfoAboutObject(referent->GetCoreType())
64 << " doesn't suit predicate";
65 return false;
66 }
67
68 return !gc_->IsMarkedEx(referent->GetCoreType());
69 }
70
HandleReference(GC * gc,GCMarkingStackType * objectsStack,const BaseClass * cls,const ObjectHeader * object,const ReferenceProcessPredicateT & pred)71 void EtsReferenceProcessor::HandleReference(GC *gc, GCMarkingStackType *objectsStack, const BaseClass *cls,
72 const ObjectHeader *object,
73 [[maybe_unused]] const ReferenceProcessPredicateT &pred)
74 {
75 LOG(DEBUG, REF_PROC) << GetDebugInfoAboutObject(object) << " is added to weak references set for processing";
76 weakReferences_.Insert(const_cast<ObjectHeader *>(object));
77 HandleOtherFields<false>(cls, object, [gc, objectsStack, object](void *reference) {
78 auto *refObject = reinterpret_cast<ObjectHeader *>(reference);
79 if (gc->MarkObjectIfNotMarked(refObject)) {
80 objectsStack->PushToStack(object, refObject);
81 }
82 });
83 }
84
HandleReference(GC * gc,const BaseClass * cls,const ObjectHeader * object,const ReferenceProcessorT & processor)85 void EtsReferenceProcessor::HandleReference([[maybe_unused]] GC *gc, const BaseClass *cls, const ObjectHeader *object,
86 const ReferenceProcessorT &processor)
87 {
88 LOG(DEBUG, REF_PROC) << GetDebugInfoAboutObject(object) << " is added to weak references set for processing";
89 weakReferences_.Insert(const_cast<ObjectHeader *>(object));
90 HandleOtherFields<true>(cls, object, processor);
91 }
92
93 template <bool USE_OBJECT_REF>
HandleOtherFields(const BaseClass * cls,const ObjectHeader * object,const ReferenceProcessorT & processor)94 void EtsReferenceProcessor::HandleOtherFields(const BaseClass *cls, const ObjectHeader *object,
95 const ReferenceProcessorT &processor)
96 {
97 auto *etsClass = ark::ets::EtsClass::FromRuntimeClass(static_cast<const Class *>(cls));
98 if (!etsClass->IsFinalizerReference()) {
99 return;
100 }
101 // Currently, only finalizer references' other fields are handled
102 ASSERT(etsClass->IsWeakReference());
103 auto *finalizableWeakRef = ark::ets::EtsFinalizableWeakRef::FromCoreType(object);
104 if constexpr (USE_OBJECT_REF) {
105 auto refHandler = [processor, finalizableWeakRef](auto *ref, size_t offset) {
106 if (ref == nullptr) {
107 return;
108 }
109 ASSERT(ref->GetReferent() != nullptr);
110 processor(ToVoidPtr(ToUintPtr(finalizableWeakRef) + offset));
111 };
112 refHandler(finalizableWeakRef->GetPrev(), ark::ets::EtsFinalizableWeakRef::GetPrevOffset());
113 refHandler(finalizableWeakRef->GetNext(), ark::ets::EtsFinalizableWeakRef::GetNextOffset());
114 } else {
115 auto refHandler = [processor](auto *ref) {
116 if (ref == nullptr) {
117 return;
118 }
119 ASSERT(ref->GetReferent() != nullptr);
120 processor(ref->GetCoreType());
121 };
122 refHandler(finalizableWeakRef->GetPrev());
123 refHandler(finalizableWeakRef->GetNext());
124 }
125 }
126
ProcessReferences(bool concurrent,bool clearSoftReferences,GCPhase gcPhase,const mem::GC::ReferenceClearPredicateT & pred)127 void EtsReferenceProcessor::ProcessReferences([[maybe_unused]] bool concurrent,
128 [[maybe_unused]] bool clearSoftReferences,
129 [[maybe_unused]] GCPhase gcPhase,
130 const mem::GC::ReferenceClearPredicateT &pred)
131 {
132 ProcessReferences(pred, [this](auto *weakRefObj, auto *referentObj) {
133 if (gc_->IsMarked(referentObj)) {
134 LOG(DEBUG, REF_PROC) << "Don't process reference " << GetDebugInfoAboutObject(weakRefObj)
135 << " because referent " << GetDebugInfoAboutObject(referentObj) << " is marked";
136 return;
137 }
138 LOG(DEBUG, REF_PROC) << "In " << GetDebugInfoAboutObject(weakRefObj) << " clear referent";
139 auto *weakRef = static_cast<ark::ets::EtsWeakReference *>(ark::ets::EtsObject::FromCoreType(weakRefObj));
140 weakRef->ClearReferent();
141 EnqueueFinalizer(weakRef);
142 });
143 }
144
ProcessReferencesAfterCompaction(const mem::GC::ReferenceClearPredicateT & pred)145 void EtsReferenceProcessor::ProcessReferencesAfterCompaction(const mem::GC::ReferenceClearPredicateT &pred)
146 {
147 ProcessReferences(pred, [this](auto *weakRefObj, auto *referentObj) {
148 auto *weakRef = static_cast<ark::ets::EtsWeakReference *>(ark::ets::EtsObject::FromCoreType(weakRefObj));
149 auto markword = referentObj->AtomicGetMark(std::memory_order_relaxed);
150 auto forwarded = markword.GetState() == MarkWord::ObjectState::STATE_GC;
151 if (forwarded) {
152 LOG(DEBUG, REF_PROC) << "Update " << GetDebugInfoAboutObject(weakRefObj) << " referent "
153 << GetDebugInfoAboutObject(referentObj) << " with forward address";
154 auto *newAddr = reinterpret_cast<ObjectHeader *>(markword.GetForwardingAddress());
155 weakRef->SetReferent(ark::ets::EtsObject::FromCoreType(newAddr));
156 return;
157 }
158 LOG(DEBUG, REF_PROC) << "In " << GetDebugInfoAboutObject(weakRefObj) << " clear referent";
159 weakRef->ClearReferent();
160 EnqueueFinalizer(weakRef);
161 });
162 }
163
GetReferenceQueueSize() const164 size_t EtsReferenceProcessor::GetReferenceQueueSize() const
165 {
166 return weakReferences_.Size();
167 }
168
ProcessFinalizers()169 void EtsReferenceProcessor::ProcessFinalizers()
170 {
171 auto finalizerIt = finalizerQueue_.begin();
172 while (finalizerIt != finalizerQueue_.end()) {
173 finalizerIt->Run();
174 finalizerIt = finalizerQueue_.erase(finalizerIt);
175 }
176 }
177
EnqueueFinalizer(ark::ets::EtsWeakReference * weakRef)178 void EtsReferenceProcessor::EnqueueFinalizer(ark::ets::EtsWeakReference *weakRef)
179 {
180 auto *weakRefClass = weakRef->GetClass();
181 if (weakRefClass->IsFinalizerReference()) {
182 auto *finalizableWeakRef = ark::ets::EtsFinalizableWeakRef::FromEtsObject(weakRef);
183 auto finalizer = finalizableWeakRef->ReleaseFinalizer();
184 if (!finalizer.IsEmpty()) {
185 finalizerQueue_.push_back(finalizer);
186 }
187 }
188 }
189
190 } // namespace ark::mem::ets
191