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 "common_interfaces/objects/base_string_table.h"
17
18 #include "common_components/base/globals.h"
19 #include "common_interfaces/objects/composite_base_class.h"
20 #include "common_components/objects/string_table/hashtriemap.h"
21 #include "common_components/objects/string_table/hashtriemap-inl.h"
22 #include "common_components/objects/string_table_internal.h"
23 #include "common_components/taskpool/taskpool.h"
24 #include "common_components/mutator/thread_local.h"
25 #include "common_interfaces/objects/base_string.h"
26 #include "common_interfaces/thread/thread_holder.h"
27 #include "common_interfaces/thread/thread_state_transition.h"
28 #include "heap/heap_allocator.h"
29
30 namespace common {
31 template <bool ConcurrentSweep>
AllocateLineStringObject(size_t size)32 BaseString* BaseStringTableInternal<ConcurrentSweep>::AllocateLineStringObject(size_t size)
33 {
34 size = AlignUp(size, ALIGN_OBJECT);
35 BaseString* str =
36 reinterpret_cast<BaseString*>(HeapAllocator::AllocateInOldOrHuge(size, LanguageType::DYNAMIC));
37 BaseClass* cls = BaseRuntime::GetInstance()->GetBaseClassRoots().GetBaseClass(CommonType::LINE_STRING);
38 str->SetFullBaseClassWithoutBarrier(cls);
39 return str;
40 }
41
42 template <bool ConcurrentSweep>
GetOrInternFlattenString(ThreadHolder * holder,const HandleCreator & handleCreator,BaseString * string)43 BaseString* BaseStringTableInternal<ConcurrentSweep>::GetOrInternFlattenString(
44 ThreadHolder* holder, const HandleCreator& handleCreator,
45 BaseString* string)
46 {
47 ASSERT(string->NotTreeString());
48 if (string->IsInternString()) {
49 return string;
50 }
51 auto readBarrier = [](void* obj, size_t offset)-> BaseObject* {
52 return BaseObject::Cast(
53 reinterpret_cast<MAddress>(BaseRuntime::ReadBarrier(
54 obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + offset))));
55 };
56 uint32_t hashcode = string->GetHashcode(readBarrier);
57 // Strings in string table should not be in the young space.
58 auto loadResult = stringTable_.template Load(readBarrier, hashcode, string);
59 if (loadResult.value != nullptr) {
60 return loadResult.value;
61 }
62 ReadOnlyHandle<BaseString> stringHandle = handleCreator(holder, string);
63 BaseString* result = stringTable_.template StoreOrLoad(
64 holder, readBarrier, hashcode, loadResult, stringHandle);
65 ASSERT(result != nullptr);
66 return result;
67 }
68
69 template <bool ConcurrentSweep>
GetOrInternStringFromCompressedSubString(ThreadHolder * holder,const HandleCreator & handleCreator,const ReadOnlyHandle<BaseString> & string,uint32_t offset,uint32_t utf8Len)70 BaseString* BaseStringTableInternal<ConcurrentSweep>::GetOrInternStringFromCompressedSubString(ThreadHolder* holder,
71 const HandleCreator& handleCreator,
72 const ReadOnlyHandle<BaseString>& string,
73 uint32_t offset, uint32_t utf8Len)
74 {
75 const uint8_t* utf8Data = string->GetDataUtf8() + offset;
76 uint32_t hashcode = BaseString::ComputeHashcodeUtf8(utf8Data, utf8Len, true);
77 auto readBarrier = [](void* obj, size_t offset)-> BaseObject* {
78 return BaseObject::Cast(
79 reinterpret_cast<MAddress>(BaseRuntime::ReadBarrier(
80 obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + offset))));
81 };
82 auto loadResult = stringTable_.template Load(readBarrier, hashcode, string, offset, utf8Len);
83 if (loadResult.value != nullptr) {
84 return loadResult.value;
85 }
86 auto allocator = [](size_t size, CommonType type)-> BaseString* {
87 ASSERT(type == CommonType::LINE_STRING);
88 return AllocateLineStringObject(size);
89 };
90 BaseString* result = stringTable_.template StoreOrLoad(
91 holder, hashcode, loadResult,
92 [holder, string, offset, utf8Len, hashcode, handleCreator, allocator]() {
93 BaseString* str = BaseString::CreateFromUtf8CompressedSubString(
94 std::move(allocator), string, offset, utf8Len);
95 str->SetMixHashcode(hashcode);
96 ASSERT(!str->IsInternString());
97 ASSERT(str->NotTreeString());
98 // Strings in string table should not be in the young space.
99 ReadOnlyHandle<BaseString> strHandle = handleCreator(holder, str);
100 return strHandle;
101 },
102 [utf8Len, string, offset](const BaseString* foundString) {
103 const uint8_t* utf8Data = string->GetDataUtf8() + offset;
104 auto readBarrier = [](void* obj, size_t offset)-> BaseObject* {
105 return BaseObject::Cast(
106 reinterpret_cast<MAddress>(BaseRuntime::ReadBarrier(
107 obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + offset))));
108 };
109 return BaseString::StringIsEqualUint8Data(readBarrier, foundString, utf8Data, utf8Len, true);
110 });
111 ASSERT(result != nullptr);
112 return result;
113 }
114
115 template <bool ConcurrentSweep>
GetOrInternString(ThreadHolder * holder,const HandleCreator & handleCreator,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress)116 BaseString* BaseStringTableInternal<ConcurrentSweep>::GetOrInternString(ThreadHolder* holder,
117 const HandleCreator& handleCreator,
118 const uint8_t* utf8Data,
119 uint32_t utf8Len,
120 bool canBeCompress)
121 {
122 uint32_t hashcode = BaseString::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress);
123 auto allocator = [](size_t size, CommonType type)-> BaseString* {
124 ASSERT(type == CommonType::LINE_STRING);
125 return AllocateLineStringObject(size);
126 };
127 BaseString* result = stringTable_.template LoadOrStore<true>(
128 holder, hashcode,
129 [holder, hashcode, utf8Data, utf8Len, canBeCompress, handleCreator, allocator]() {
130 BaseString* value = BaseString::CreateFromUtf8(std::move(allocator), utf8Data, utf8Len, canBeCompress);
131 value->SetMixHashcode(hashcode);
132 ASSERT(!value->IsInternString());
133 ASSERT(value->NotTreeString());
134 ReadOnlyHandle<BaseString> stringHandle = handleCreator(holder, value);
135 return stringHandle;
136 },
137 [utf8Data, utf8Len, canBeCompress](BaseString* foundString) {
138 auto readBarrier = [](void* obj, size_t offset)-> BaseObject* {
139 return BaseObject::Cast(
140 reinterpret_cast<MAddress>(BaseRuntime::ReadBarrier(
141 obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + offset))));
142 };
143 return BaseString::StringIsEqualUint8Data(readBarrier, foundString, utf8Data, utf8Len,
144 canBeCompress);
145 });
146 ASSERT(result != nullptr);
147 return result;
148 }
149
150 template <bool ConcurrentSweep>
GetOrInternString(ThreadHolder * holder,const HandleCreator & handleCreator,const uint16_t * utf16Data,uint32_t utf16Len,bool canBeCompress)151 BaseString* BaseStringTableInternal<ConcurrentSweep>::GetOrInternString(
152 ThreadHolder* holder, const HandleCreator& handleCreator,
153 const uint16_t* utf16Data, uint32_t utf16Len,
154 bool canBeCompress)
155 {
156 uint32_t hashcode = BaseString::ComputeHashcodeUtf16(const_cast<uint16_t*>(utf16Data), utf16Len);
157 auto allocator = [](size_t size, CommonType type)-> BaseString* {
158 ASSERT(type == CommonType::LINE_STRING);
159 return AllocateLineStringObject(size);
160 };
161 BaseString* result = stringTable_.template LoadOrStore<true>(
162 holder, hashcode,
163 [holder, utf16Data, utf16Len, canBeCompress, hashcode, handleCreator, allocator]() {
164 BaseString* value = BaseString::CreateFromUtf16(std::move(allocator), utf16Data, utf16Len,
165 canBeCompress);
166 value->SetMixHashcode(hashcode);
167 ASSERT(!value->IsInternString());
168 ASSERT(value->NotTreeString());
169 // Strings in string table should not be in the young space.
170 ReadOnlyHandle<BaseString> stringHandle = handleCreator(holder, value);
171 return stringHandle;
172 },
173 [utf16Data, utf16Len](BaseString* foundString) {
174 auto readBarrier = [](void* obj, size_t offset)-> BaseObject* {
175 return BaseObject::Cast(
176 reinterpret_cast<MAddress>(BaseRuntime::ReadBarrier(
177 obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + offset))));
178 };
179 return BaseString::StringsAreEqualUtf16(readBarrier, foundString, utf16Data, utf16Len);
180 });
181 ASSERT(result != nullptr);
182 return result;
183 }
184
185 template <bool ConcurrentSweep>
TryGetInternString(const ReadOnlyHandle<BaseString> & string)186 BaseString* BaseStringTableInternal<ConcurrentSweep>::TryGetInternString(const ReadOnlyHandle<BaseString>& string)
187 {
188 auto readBarrier = [](void* obj, size_t offset)-> BaseObject* {
189 return BaseObject::Cast(
190 reinterpret_cast<MAddress>(BaseRuntime::ReadBarrier(
191 obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + offset))));
192 };
193 uint32_t hashcode = string->GetHashcode(readBarrier);
194 return stringTable_.template Load<false>(readBarrier, hashcode, *string);
195 }
196
197 template <bool ConcurrentSweep>
198 template <bool B, std::enable_if_t<B, int>>
SweepWeakRef(const WeakRefFieldVisitor & visitor,uint32_t rootID,std::vector<HashTrieMapEntry * > & waitDeleteEntries)199 void BaseStringTableInternal<ConcurrentSweep>::SweepWeakRef(const WeakRefFieldVisitor& visitor, uint32_t rootID,
200 std::vector<HashTrieMapEntry*>& waitDeleteEntries)
201 {
202 ASSERT(rootID >= 0 && rootID < TrieMapConfig::ROOT_SIZE);
203 auto rootNode = stringTable_.root_[rootID].load(std::memory_order_relaxed);
204 if (rootNode == nullptr) {
205 return;
206 }
207 for (uint32_t index = 0; index < TrieMapConfig::INDIRECT_SIZE; ++index) {
208 stringTable_.ClearNodeFromGC(rootNode, index, visitor, waitDeleteEntries);
209 }
210 }
211
212 template <bool ConcurrentSweep>
213 template <bool B, std::enable_if_t<B, int>>
CleanUp()214 void BaseStringTableInternal<ConcurrentSweep>::CleanUp()
215 {
216 stringTable_.CleanUp();
217 }
218
219 template <bool ConcurrentSweep>
220 template <bool B, std::enable_if_t<!B, int>>
SweepWeakRef(const WeakRefFieldVisitor & visitor)221 void BaseStringTableInternal<ConcurrentSweep>::SweepWeakRef(const WeakRefFieldVisitor& visitor)
222 {
223 // No need lock here, only shared gc will sweep string table, meanwhile other threads are suspended.
224 for (uint32_t rootID = 0; rootID < TrieMapConfig::ROOT_SIZE; ++rootID) {
225 auto rootNode = stringTable_.root_[rootID].load(std::memory_order_relaxed);
226 if (rootNode == nullptr) {
227 continue;
228 }
229 for (uint32_t index = 0; index < TrieMapConfig::INDIRECT_SIZE; ++index) {
230 stringTable_.ClearNodeFromGC(rootNode, index, visitor);
231 }
232 }
233 }
234
235 template void BaseStringTableInternal<false>::SweepWeakRef<false>(const WeakRefFieldVisitor& visitor);
236
GetOrInternFlattenString(ThreadHolder * holder,const HandleCreator & handleCreator,BaseString * string)237 BaseString* BaseStringTableImpl::GetOrInternFlattenString(ThreadHolder* holder, const HandleCreator& handleCreator,
238 BaseString* string)
239 {
240 return stringTable_.GetOrInternFlattenString(holder, handleCreator, string);
241 }
242
GetOrInternStringFromCompressedSubString(ThreadHolder * holder,const HandleCreator & handleCreator,const ReadOnlyHandle<BaseString> & string,uint32_t offset,uint32_t utf8Len)243 BaseString* BaseStringTableImpl::GetOrInternStringFromCompressedSubString(
244 ThreadHolder* holder, const HandleCreator& handleCreator,
245 const ReadOnlyHandle<BaseString>& string, uint32_t offset, uint32_t utf8Len)
246 {
247 return stringTable_.GetOrInternStringFromCompressedSubString(holder, handleCreator, string, offset, utf8Len);
248 }
249
GetOrInternString(ThreadHolder * holder,const HandleCreator & handleCreator,const uint8_t * utf8Data,uint32_t utf8Len,bool canBeCompress)250 BaseString* BaseStringTableImpl::GetOrInternString(ThreadHolder* holder, const HandleCreator& handleCreator,
251 const uint8_t* utf8Data, uint32_t utf8Len, bool canBeCompress)
252 {
253 return stringTable_.GetOrInternString(holder, handleCreator, utf8Data, utf8Len, canBeCompress);
254 }
255
GetOrInternString(ThreadHolder * holder,const HandleCreator & handleCreator,const uint16_t * utf16Data,uint32_t utf16Len,bool canBeCompress)256 BaseString* BaseStringTableImpl::GetOrInternString(ThreadHolder* holder, const HandleCreator& handleCreator,
257 const uint16_t* utf16Data, uint32_t utf16Len, bool canBeCompress)
258 {
259 return stringTable_.GetOrInternString(holder, handleCreator, utf16Data, utf16Len, canBeCompress);
260 }
261
TryGetInternString(const ReadOnlyHandle<BaseString> & string)262 BaseString* BaseStringTableImpl::TryGetInternString(const ReadOnlyHandle<BaseString>& string)
263 {
264 return stringTable_.TryGetInternString(string);
265 }
266
StartSweepWeakRefTask()267 void BaseStringTableCleaner::StartSweepWeakRefTask()
268 {
269 // No need lock here, only the daemon thread will reset the state.
270 sweepWeakRefFinished_ = false;
271 PendingTaskCount_.store(TrieMapConfig::ROOT_SIZE, std::memory_order_relaxed);
272 }
273
WaitSweepWeakRefTask()274 void BaseStringTableCleaner::WaitSweepWeakRefTask()
275 {
276 LockHolder holder(sweepWeakRefMutex_);
277 while (!sweepWeakRefFinished_) {
278 sweepWeakRefCV_.Wait(&sweepWeakRefMutex_);
279 }
280 }
281
SignalSweepWeakRefTask()282 void BaseStringTableCleaner::SignalSweepWeakRefTask()
283 {
284 LockHolder holder(sweepWeakRefMutex_);
285 sweepWeakRefFinished_ = true;
286 sweepWeakRefCV_.SignalAll();
287 }
288
289
PostSweepWeakRefTask(const WeakRefFieldVisitor & visitor)290 void BaseStringTableCleaner::PostSweepWeakRefTask(const WeakRefFieldVisitor &visitor)
291 {
292 StartSweepWeakRefTask();
293 stringTable_->GetHashTrieMap().StartSweeping();
294 iter_ = std::make_shared<std::atomic<uint32_t>>(0U);
295 const uint32_t postTaskCount =
296 Taskpool::GetCurrentTaskpool()->GetTotalThreadNum();
297 for (uint32_t i = 0U; i < postTaskCount; ++i) {
298 Taskpool::GetCurrentTaskpool()->PostTask(
299 std::make_unique<CMCSweepWeakRefTask>(iter_, this, visitor));
300 }
301 }
302
JoinAndWaitSweepWeakRefTask(const WeakRefFieldVisitor & visitor)303 void BaseStringTableCleaner::JoinAndWaitSweepWeakRefTask(
304 const WeakRefFieldVisitor &visitor)
305 {
306 ProcessSweepWeakRef(iter_, this, visitor);
307 WaitSweepWeakRefTask();
308 iter_.reset();
309 stringTable_->GetHashTrieMap().FinishSweeping();
310 }
311
CleanUp()312 void BaseStringTableCleaner::CleanUp()
313 {
314 for (std::vector<HashTrieMapEntry*>& waitFrees : waitFreeEntries_) {
315 for (const HashTrieMapEntry* entry : waitFrees) {
316 delete entry;
317 }
318 waitFrees.clear();
319 }
320 stringTable_->CleanUp();
321 }
322
ProcessSweepWeakRef(IteratorPtr & iter,BaseStringTableCleaner * cleaner,const WeakRefFieldVisitor & visitor)323 void BaseStringTableCleaner::ProcessSweepWeakRef(
324 IteratorPtr &iter, BaseStringTableCleaner *cleaner,
325 const WeakRefFieldVisitor &visitor)
326 {
327 uint32_t index = 0U;
328 while ((index = GetNextIndexId(iter)) < TrieMapConfig::ROOT_SIZE) {
329 cleaner->waitFreeEntries_[index].clear();
330 cleaner->stringTable_->SweepWeakRef(visitor, index, cleaner->waitFreeEntries_[index]);
331 if (ReduceCountAndCheckFinish(cleaner)) {
332 cleaner->SignalSweepWeakRefTask();
333 }
334 }
335 }
336
Run(uint32_t threadIndex)337 bool BaseStringTableCleaner::CMCSweepWeakRefTask::Run([[maybe_unused]] uint32_t threadIndex)
338 {
339 ThreadLocal::SetThreadType(ThreadType::GC_THREAD);
340 ProcessSweepWeakRef(iter_, cleaner_, visitor_);
341 ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR);
342 ThreadLocal::ClearAllocBufferRegion();
343 return true;
344 }
345 }
346