/* * 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. */ #include "common_interfaces/objects/base_string_table.h" #include "common_components/base/globals.h" #include "common_interfaces/objects/composite_base_class.h" #include "common_components/objects/string_table/hashtriemap.h" #include "common_components/objects/string_table/hashtriemap-inl.h" #include "common_components/objects/string_table_internal.h" #include "common_components/taskpool/taskpool.h" #include "common_components/mutator/thread_local.h" #include "common_interfaces/objects/base_string.h" #include "common_interfaces/thread/thread_holder.h" #include "common_interfaces/thread/thread_state_transition.h" #include "heap/heap_allocator.h" namespace common { template BaseString* BaseStringTableInternal::AllocateLineStringObject(size_t size) { size = AlignUp(size, ALIGN_OBJECT); BaseString* str = reinterpret_cast(HeapAllocator::AllocateInOldOrHuge(size, LanguageType::DYNAMIC)); BaseClass* cls = BaseRuntime::GetInstance()->GetBaseClassRoots().GetBaseClass(CommonType::LINE_STRING); str->SetFullBaseClassWithoutBarrier(cls); return str; } template BaseString* BaseStringTableInternal::GetOrInternFlattenString( ThreadHolder* holder, const HandleCreator& handleCreator, BaseString* string) { ASSERT(string->NotTreeString()); if (string->IsInternString()) { return string; } auto readBarrier = [](void* obj, size_t offset)-> BaseObject* { return BaseObject::Cast( reinterpret_cast(BaseRuntime::ReadBarrier( obj, reinterpret_cast(reinterpret_cast(obj) + offset)))); }; uint32_t hashcode = string->GetHashcode(readBarrier); // Strings in string table should not be in the young space. auto loadResult = stringTable_.template Load(readBarrier, hashcode, string); if (loadResult.value != nullptr) { return loadResult.value; } ReadOnlyHandle stringHandle = handleCreator(holder, string); BaseString* result = stringTable_.template StoreOrLoad( holder, readBarrier, hashcode, loadResult, stringHandle); ASSERT(result != nullptr); return result; } template BaseString* BaseStringTableInternal::GetOrInternStringFromCompressedSubString(ThreadHolder* holder, const HandleCreator& handleCreator, const ReadOnlyHandle& string, uint32_t offset, uint32_t utf8Len) { const uint8_t* utf8Data = string->GetDataUtf8() + offset; uint32_t hashcode = BaseString::ComputeHashcodeUtf8(utf8Data, utf8Len, true); auto readBarrier = [](void* obj, size_t offset)-> BaseObject* { return BaseObject::Cast( reinterpret_cast(BaseRuntime::ReadBarrier( obj, reinterpret_cast(reinterpret_cast(obj) + offset)))); }; auto loadResult = stringTable_.template Load(readBarrier, hashcode, string, offset, utf8Len); if (loadResult.value != nullptr) { return loadResult.value; } auto allocator = [](size_t size, CommonType type)-> BaseString* { ASSERT(type == CommonType::LINE_STRING); return AllocateLineStringObject(size); }; BaseString* result = stringTable_.template StoreOrLoad( holder, hashcode, loadResult, [holder, string, offset, utf8Len, hashcode, handleCreator, allocator]() { BaseString* str = BaseString::CreateFromUtf8CompressedSubString( std::move(allocator), string, offset, utf8Len); str->SetMixHashcode(hashcode); ASSERT(!str->IsInternString()); ASSERT(str->NotTreeString()); // Strings in string table should not be in the young space. ReadOnlyHandle strHandle = handleCreator(holder, str); return strHandle; }, [utf8Len, string, offset](const BaseString* foundString) { const uint8_t* utf8Data = string->GetDataUtf8() + offset; auto readBarrier = [](void* obj, size_t offset)-> BaseObject* { return BaseObject::Cast( reinterpret_cast(BaseRuntime::ReadBarrier( obj, reinterpret_cast(reinterpret_cast(obj) + offset)))); }; return BaseString::StringIsEqualUint8Data(readBarrier, foundString, utf8Data, utf8Len, true); }); ASSERT(result != nullptr); return result; } template BaseString* BaseStringTableInternal::GetOrInternString(ThreadHolder* holder, const HandleCreator& handleCreator, const uint8_t* utf8Data, uint32_t utf8Len, bool canBeCompress) { uint32_t hashcode = BaseString::ComputeHashcodeUtf8(utf8Data, utf8Len, canBeCompress); auto allocator = [](size_t size, CommonType type)-> BaseString* { ASSERT(type == CommonType::LINE_STRING); return AllocateLineStringObject(size); }; BaseString* result = stringTable_.template LoadOrStore( holder, hashcode, [holder, hashcode, utf8Data, utf8Len, canBeCompress, handleCreator, allocator]() { BaseString* value = BaseString::CreateFromUtf8(std::move(allocator), utf8Data, utf8Len, canBeCompress); value->SetMixHashcode(hashcode); ASSERT(!value->IsInternString()); ASSERT(value->NotTreeString()); ReadOnlyHandle stringHandle = handleCreator(holder, value); return stringHandle; }, [utf8Data, utf8Len, canBeCompress](BaseString* foundString) { auto readBarrier = [](void* obj, size_t offset)-> BaseObject* { return BaseObject::Cast( reinterpret_cast(BaseRuntime::ReadBarrier( obj, reinterpret_cast(reinterpret_cast(obj) + offset)))); }; return BaseString::StringIsEqualUint8Data(readBarrier, foundString, utf8Data, utf8Len, canBeCompress); }); ASSERT(result != nullptr); return result; } template BaseString* BaseStringTableInternal::GetOrInternString( ThreadHolder* holder, const HandleCreator& handleCreator, const uint16_t* utf16Data, uint32_t utf16Len, bool canBeCompress) { uint32_t hashcode = BaseString::ComputeHashcodeUtf16(const_cast(utf16Data), utf16Len); auto allocator = [](size_t size, CommonType type)-> BaseString* { ASSERT(type == CommonType::LINE_STRING); return AllocateLineStringObject(size); }; BaseString* result = stringTable_.template LoadOrStore( holder, hashcode, [holder, utf16Data, utf16Len, canBeCompress, hashcode, handleCreator, allocator]() { BaseString* value = BaseString::CreateFromUtf16(std::move(allocator), utf16Data, utf16Len, canBeCompress); value->SetMixHashcode(hashcode); ASSERT(!value->IsInternString()); ASSERT(value->NotTreeString()); // Strings in string table should not be in the young space. ReadOnlyHandle stringHandle = handleCreator(holder, value); return stringHandle; }, [utf16Data, utf16Len](BaseString* foundString) { auto readBarrier = [](void* obj, size_t offset)-> BaseObject* { return BaseObject::Cast( reinterpret_cast(BaseRuntime::ReadBarrier( obj, reinterpret_cast(reinterpret_cast(obj) + offset)))); }; return BaseString::StringsAreEqualUtf16(readBarrier, foundString, utf16Data, utf16Len); }); ASSERT(result != nullptr); return result; } template BaseString* BaseStringTableInternal::TryGetInternString(const ReadOnlyHandle& string) { auto readBarrier = [](void* obj, size_t offset)-> BaseObject* { return BaseObject::Cast( reinterpret_cast(BaseRuntime::ReadBarrier( obj, reinterpret_cast(reinterpret_cast(obj) + offset)))); }; uint32_t hashcode = string->GetHashcode(readBarrier); return stringTable_.template Load(readBarrier, hashcode, *string); } template template > void BaseStringTableInternal::SweepWeakRef(const WeakRefFieldVisitor& visitor, uint32_t rootID, std::vector& waitDeleteEntries) { ASSERT(rootID >= 0 && rootID < TrieMapConfig::ROOT_SIZE); auto rootNode = stringTable_.root_[rootID].load(std::memory_order_relaxed); if (rootNode == nullptr) { return; } for (uint32_t index = 0; index < TrieMapConfig::INDIRECT_SIZE; ++index) { stringTable_.ClearNodeFromGC(rootNode, index, visitor, waitDeleteEntries); } } template template > void BaseStringTableInternal::CleanUp() { stringTable_.CleanUp(); } template template > void BaseStringTableInternal::SweepWeakRef(const WeakRefFieldVisitor& visitor) { // No need lock here, only shared gc will sweep string table, meanwhile other threads are suspended. for (uint32_t rootID = 0; rootID < TrieMapConfig::ROOT_SIZE; ++rootID) { auto rootNode = stringTable_.root_[rootID].load(std::memory_order_relaxed); if (rootNode == nullptr) { continue; } for (uint32_t index = 0; index < TrieMapConfig::INDIRECT_SIZE; ++index) { stringTable_.ClearNodeFromGC(rootNode, index, visitor); } } } template void BaseStringTableInternal::SweepWeakRef(const WeakRefFieldVisitor& visitor); BaseString* BaseStringTableImpl::GetOrInternFlattenString(ThreadHolder* holder, const HandleCreator& handleCreator, BaseString* string) { return stringTable_.GetOrInternFlattenString(holder, handleCreator, string); } BaseString* BaseStringTableImpl::GetOrInternStringFromCompressedSubString( ThreadHolder* holder, const HandleCreator& handleCreator, const ReadOnlyHandle& string, uint32_t offset, uint32_t utf8Len) { return stringTable_.GetOrInternStringFromCompressedSubString(holder, handleCreator, string, offset, utf8Len); } BaseString* BaseStringTableImpl::GetOrInternString(ThreadHolder* holder, const HandleCreator& handleCreator, const uint8_t* utf8Data, uint32_t utf8Len, bool canBeCompress) { return stringTable_.GetOrInternString(holder, handleCreator, utf8Data, utf8Len, canBeCompress); } BaseString* BaseStringTableImpl::GetOrInternString(ThreadHolder* holder, const HandleCreator& handleCreator, const uint16_t* utf16Data, uint32_t utf16Len, bool canBeCompress) { return stringTable_.GetOrInternString(holder, handleCreator, utf16Data, utf16Len, canBeCompress); } BaseString* BaseStringTableImpl::TryGetInternString(const ReadOnlyHandle& string) { return stringTable_.TryGetInternString(string); } void BaseStringTableCleaner::StartSweepWeakRefTask() { // No need lock here, only the daemon thread will reset the state. sweepWeakRefFinished_ = false; PendingTaskCount_.store(TrieMapConfig::ROOT_SIZE, std::memory_order_relaxed); } void BaseStringTableCleaner::WaitSweepWeakRefTask() { LockHolder holder(sweepWeakRefMutex_); while (!sweepWeakRefFinished_) { sweepWeakRefCV_.Wait(&sweepWeakRefMutex_); } } void BaseStringTableCleaner::SignalSweepWeakRefTask() { LockHolder holder(sweepWeakRefMutex_); sweepWeakRefFinished_ = true; sweepWeakRefCV_.SignalAll(); } void BaseStringTableCleaner::PostSweepWeakRefTask(const WeakRefFieldVisitor &visitor) { StartSweepWeakRefTask(); stringTable_->GetHashTrieMap().StartSweeping(); iter_ = std::make_shared>(0U); const uint32_t postTaskCount = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum(); for (uint32_t i = 0U; i < postTaskCount; ++i) { Taskpool::GetCurrentTaskpool()->PostTask( std::make_unique(iter_, this, visitor)); } } void BaseStringTableCleaner::JoinAndWaitSweepWeakRefTask( const WeakRefFieldVisitor &visitor) { ProcessSweepWeakRef(iter_, this, visitor); WaitSweepWeakRefTask(); iter_.reset(); stringTable_->GetHashTrieMap().FinishSweeping(); } void BaseStringTableCleaner::CleanUp() { for (std::vector& waitFrees : waitFreeEntries_) { for (const HashTrieMapEntry* entry : waitFrees) { delete entry; } waitFrees.clear(); } stringTable_->CleanUp(); } void BaseStringTableCleaner::ProcessSweepWeakRef( IteratorPtr &iter, BaseStringTableCleaner *cleaner, const WeakRefFieldVisitor &visitor) { uint32_t index = 0U; while ((index = GetNextIndexId(iter)) < TrieMapConfig::ROOT_SIZE) { cleaner->waitFreeEntries_[index].clear(); cleaner->stringTable_->SweepWeakRef(visitor, index, cleaner->waitFreeEntries_[index]); if (ReduceCountAndCheckFinish(cleaner)) { cleaner->SignalSweepWeakRefTask(); } } } bool BaseStringTableCleaner::CMCSweepWeakRefTask::Run([[maybe_unused]] uint32_t threadIndex) { ThreadLocal::SetThreadType(ThreadType::GC_THREAD); ProcessSweepWeakRef(iter_, cleaner_, visitor_); ThreadLocal::SetThreadType(ThreadType::ARK_PROCESSOR); ThreadLocal::ClearAllocBufferRegion(); return true; } }