/* * 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_components/heap/collector/finalizer_processor.h" #include "common_components/common/scoped_object_access.h" #include "common_components/mutator/mutator.h" namespace common { constexpr uint32_t DEFAULT_FINALIZER_TIMEOUT_MS = 2000; // Note: can only be called by FinalizerProcessor thread extern "C" PUBLIC_API void* ArkProcessFinalizers(void* arg) { #ifdef __APPLE__ CHECK_CALL(pthread_setname_np, ("gc-helper"), "finalizer-processor thread setname"); #elif defined(__linux__) || defined(PANDA_TARGET_OHOS) CHECK_CALL(prctl, (PR_SET_NAME, "gc-helper"), "finalizer-processor thread setname"); #endif reinterpret_cast(arg)->Run(); return nullptr; } void FinalizerProcessor::Start() { pthread_t thread; pthread_attr_t attr; // size_t stackSize = ArkCommonRuntime::GetConcurrencyParam().thStackSize * KB; // default 1MB stacksize // FinalizerProcessor needs to be revied size_t stackSize = 1024 * KB; #if defined(__linux__) || defined(PANDA_TARGET_OHOS) || defined(__APPLE__) // PTHREAD_STACK_MIN is not supported in Windows. if (stackSize < static_cast(PTHREAD_STACK_MIN)) { stackSize = static_cast(PTHREAD_STACK_MIN); } #endif CHECK_CALL(pthread_attr_init, (&attr), "init pthread attr"); CHECK_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_JOINABLE), "set pthread joinable"); CHECK_CALL(pthread_attr_setstacksize, (&attr, stackSize), "set pthread stacksize"); CHECK_CALL(pthread_create, (&thread, &attr, ArkProcessFinalizers, this), "create finalizer-process thread"); #ifdef __WIN64 CHECK_CALL(pthread_setname_np, (thread, "gc-helper"), "finalizer-processor thread setname"); #endif CHECK_CALL(pthread_attr_destroy, (&attr), "destroy pthread attr"); threadHandle_ = thread; WaitStarted(); } // Stop FinalizerProcessor is only invoked at Fork or Runtime finliazaiton // Should only invoke once. void FinalizerProcessor::Stop() { // This will only occur in the prefork scenario. if (running_ == false) { return; } running_ = false; Notify(); WaitStop(); } FinalizerProcessor::FinalizerProcessor() { started_ = false; running_ = false; iterationWaitTime_ = DEFAULT_FINALIZER_TIMEOUT_MS; timeProcessorBegin_ = 0; timeProcessUsed_ = 0; timeCurrentProcessBegin_ = 0; hasFinalizableJob_.store(false, std::memory_order_relaxed); shouldReclaimHeapGarbage_.store(false, std::memory_order_relaxed); shouldFeedHungryBuffers_.store(false, std::memory_order_relaxed); } void FinalizerProcessor::Run() { Init(); NotifyStarted(); while (running_) { if (hasFinalizableJob_.load(std::memory_order_relaxed)) { // In theory we currently won't have this ProcessFinalizables(); #if defined(GCINFO_DEBUG) && GCINFO_DEBUG LogAfterProcess(); #endif } if (shouldFeedHungryBuffers_.load(std::memory_order_relaxed)) { FeedHungryBuffers(); } if (shouldReclaimHeapGarbage_.load(std::memory_order_relaxed)) { ReclaimHeapGarbage(); } { COMMON_PHASE_TIMER("finalizerProcessor waitting time"); while (running_) { Wait(iterationWaitTime_); if (hasFinalizableJob_.load(std::memory_order_relaxed) || shouldReclaimHeapGarbage_.load(std::memory_order_relaxed) || shouldFeedHungryBuffers_.load(std::memory_order_relaxed)) { break; } } } } Fini(); } void FinalizerProcessor::Init() { Mutator* mutator = MutatorManager::Instance().CreateRuntimeMutator(ThreadType::FP_THREAD); (void)mutator->EnterSaferegion(true); tid_ = mutator->GetTid(); ThreadLocal::SetProtectAddr(reinterpret_cast(0)); running_ = true; timeProcessorBegin_ = TimeUtil::MicroSeconds(); timeProcessUsed_ = 0; MutatorManager::Instance().MutatorManagementRLock(); fpMutator_ = mutator; MutatorManager::Instance().MutatorManagementRUnlock(); VLOG(INFO, "FinalizerProcessor thread started"); } void FinalizerProcessor::Fini() { MutatorManager::Instance().MutatorManagementRLock(); fpMutator_ = nullptr; MutatorManager::Instance().MutatorManagementRUnlock(); MutatorManager::Instance().DestroyRuntimeMutator(ThreadType::FP_THREAD); VLOG(INFO, "FinalizerProcessor thread stopped"); } void FinalizerProcessor::WaitStop() { pthread_t thread = threadHandle_; int tmpResult = ::pthread_join(thread, nullptr); LOGF_CHECK(tmpResult == 0) << "::pthread_join() in FinalizerProcessor::WaitStop() return " << tmpResult << " rather than 0."; started_ = false; threadHandle_ = 0; } void FinalizerProcessor::Notify() { wakeCondition_.notify_one(); } void FinalizerProcessor::Wait(uint32_t timeoutMilliSeconds) { std::unique_lock lock(wakeLock_); std::chrono::milliseconds epoch(timeoutMilliSeconds); wakeCondition_.wait_for(lock, epoch); } void FinalizerProcessor::NotifyStarted() { { std::unique_lock lock(startedLock_); LOGF_CHECK(started_ != true) << "unpexcted true, FinalizerProcessor might not wait stopped"; started_ = true; } startedCondition_.notify_all(); } void FinalizerProcessor::WaitStarted() { std::unique_lock lock(startedLock_); if (started_) { return; } startedCondition_.wait(lock, [this] { return started_; }); } void FinalizerProcessor::EnqueueFinalizables(const std::function& finalizable, uint32_t countLimit) { std::lock_guard l(listLock_); auto it = finalizers_.begin(); while (it != finalizers_.end() && countLimit != 0) { RefField<> tmpField(reinterpret_cast(*it)); BaseObject* obj = tmpField.GetTargetObject(); --countLimit; if (finalizable(obj)) { finalizables_.push_back(reinterpret_cast(tmpField.GetFieldValue())); it = finalizers_.erase(it); } else { ++it; } } if (!finalizables_.empty()) { hasFinalizableJob_.store(true, std::memory_order_relaxed); } } // Process finalizable list // 1. always process list head // 2. Leave safe region (calling in finalizerProcessor thread) // 3. Invoke finalize method // 4. remove processed finalizables void FinalizerProcessor::ProcessFinalizableList() { LOG_COMMON(FATAL) << "Unresolved fatal"; UNREACHABLE_CC(); } void FinalizerProcessor::ProcessFinalizables() { COMMON_PHASE_TIMER("Finalizer"); { // we leave saferegion to avoid GC visit those changing queues. ScopedObjectAccess soa; std::lock_guard l(listLock_); // FP will not come here before cleaning up workingFinalizables // workingFinalizables is expected empty, thus we could use std::swap here workingFinalizables_.swap(finalizables_); } DLOG(FINALIZE, "finalizer: working size %zu", workingFinalizables_.size()); ProcessFinalizableList(); if (finalizables_.empty()) { hasFinalizableJob_.store(false, std::memory_order_relaxed); } } #if defined(GCINFO_DEBUG) && GCINFO_DEBUG void FinalizerProcessor::LogAfterProcess() { if (!ENABLE_LOG(FINALIZE)) { return; } uint64_t timeNow = TimeUtil::MicroSeconds(); uint64_t timeConsumed = timeNow - timeCurrentProcessBegin_; uint64_t totalTimePassed = timeNow - timeProcessorBegin_; timeProcessUsed_ += timeConsumed; constexpr float percentageDivend = 100.0f; float percentage = (static_cast(TIME_FACTOR * timeProcessUsed_) / totalTimePassed) / percentageDivend; DLOG(FINALIZE, "[FinalizerProcessor] End (%luus [%luus] [%.2f%%])", timeConsumed, timeProcessUsed_, percentage); } #endif void FinalizerProcessor::RegisterFinalizer(BaseObject* obj) { RefField<> tmpField(nullptr); Heap::GetHeap().GetBarrier().WriteStaticRef(tmpField, obj); std::lock_guard l(listLock_); finalizers_.push_back(reinterpret_cast(tmpField.GetFieldValue())); } void FinalizerProcessor::ReclaimHeapGarbage() { OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "ARK_RT_GC_RECLAIM", ""); Heap::GetHeap().GetAllocator().ReclaimGarbageMemory(false); shouldReclaimHeapGarbage_.store(false, std::memory_order_relaxed); } void FinalizerProcessor::FeedHungryBuffers() { Heap::GetHeap().GetAllocator().FeedHungryBuffers(); shouldFeedHungryBuffers_.store(false, std::memory_order_relaxed); } } // namespace common