* 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/mutator/mutator_manager.h"
#include <thread>
#include "common_components/base/time_utils.h"
#include "common_components/heap/collector/finalizer_processor.h"
#include "common_components/heap/collector/marking_collector.h"
#include "common_components/heap/heap.h"
#include "common_components/mutator/mutator.inline.h"
namespace common {
bool g_enableGCTimeoutCheck = true;
bool IsRuntimeThread()
{
if (static_cast<int>(ThreadLocal::GetThreadType()) >= static_cast<int>(ThreadType::GC_THREAD)) {
return true;
}
return false;
}
bool IsGcThread()
{
if (static_cast<int>(ThreadLocal::GetThreadType()) == static_cast<int>(ThreadType::GC_THREAD)) {
return true;
}
return false;
}
void MutatorManager::BindMutator(Mutator& mutator) const
{
ThreadLocalData* tlData = ThreadLocal::GetThreadLocalData();
if (UNLIKELY_CC(tlData->buffer == nullptr)) {
(void)AllocationBuffer::GetOrCreateAllocBuffer();
}
mutator.SetSafepointActive(false);
tlData->mutator = &mutator;
}
void MutatorManager::UnbindMutator(Mutator& mutator) const
{
ThreadLocalData* tlData = ThreadLocal::GetThreadLocalData();
tlData->mutator = nullptr;
tlData->buffer = nullptr;
}
bool MutatorManager::BindMutatorOnly(Mutator *mutator) const
{
common::ThreadLocalData* tlData = common::ThreadLocal::GetThreadLocalData();
ASSERT(tlData != nullptr);
if (tlData->mutator == nullptr) {
tlData->mutator = mutator;
return true;
}
return false;
}
void MutatorManager::UnbindMutatorOnly() const
{
ThreadLocalData* tlData = ThreadLocal::GetThreadLocalData();
tlData->mutator = nullptr;
}
Mutator* MutatorManager::CreateMutator()
{
Mutator* mutator = ThreadLocal::GetMutator();
ASSERT_LOGF(mutator != nullptr, "Mutator already exists");
MutatorManagementRLock();
mutator->Init();
mutator->InitTid();
BindMutator(*mutator);
mutator->SetMutatorPhase(Heap::GetHeap().GetGCPhase());
MutatorManagementRUnlock();
return mutator;
}
void MutatorManager::TransitMutatorToExit()
{
Mutator* mutator = Mutator::GetMutator();
LOGF_CHECK(mutator != nullptr) << "Mutator has not initialized or has been fini: " << mutator;
ASSERT_LOGF(!mutator->InSaferegion(), "Mutator to be fini should not be in saferegion");
mutator->MutatorLock();
mutator->ResetMutator();
mutator->MutatorUnlock();
(void)mutator->EnterSaferegion(false);
UnbindMutator(*mutator);
}
void MutatorManager::DestroyExpiredMutators()
{
expiringMutatorListLock_.lock();
ExpiredMutatorList workList;
workList.swap(expiringMutators_);
expiringMutatorListLock_.unlock();
for (auto it = workList.begin(); it != workList.end(); ++it) {
Mutator* expiringMutator = *it;
delete expiringMutator;
}
}
void MutatorManager::DestroyMutator(Mutator* mutator)
{
if (TryAcquireMutatorManagementRLock()) {
delete mutator;
MutatorManagementRUnlock();
} else {
expiringMutatorListLock_.lock();
expiringMutators_.push_back(mutator);
expiringMutatorListLock_.unlock();
}
}
Mutator* MutatorManager::CreateRuntimeMutator(ThreadType threadType)
{
static Mutator fpMutatorInstance;
Mutator* mutator = nullptr;
if (threadType == ThreadType::FP_THREAD) {
mutator = &fpMutatorInstance;
} else {
mutator = new (std::nothrow) Mutator();
}
LOGF_CHECK(mutator != nullptr) << "create mutator out of native memory";
MutatorManagementRLock();
mutator->Init();
mutator->InitTid();
MutatorManager::Instance().BindMutator(*mutator);
mutator->SetMutatorPhase(Heap::GetHeap().GetGCPhase());
ThreadLocal::SetMutator(mutator);
ThreadLocal::SetThreadType(threadType);
ThreadLocal::SetProcessorFlag(true);
ThreadLocalData* threadData = GetThreadLocalData();
PreRunManagedCode(mutator, 2, threadData);
MutatorManagementRUnlock();
return mutator;
}
void MutatorManager::DestroyRuntimeMutator(ThreadType threadType)
{
Mutator* mutator = ThreadLocal::GetMutator();
LOGF_CHECK(mutator != nullptr) << "Fini UpdateThreads with null mutator";
MutatorManagementRLock();
(void)mutator->LeaveSaferegion();
if (threadType != ThreadType::FP_THREAD) {
delete mutator;
} else {
mutator->ResetMutator();
}
ThreadLocal::SetAllocBuffer(nullptr);
ThreadLocal::SetMutator(nullptr);
ThreadLocal::SetProcessorFlag(false);
MutatorManagementRUnlock();
}
void MutatorManager::Init()
{
#if defined(__linux__) || defined(PANDA_TARGET_OHOS) || defined(__APPLE__)
safepointPageManager_ = new (std::nothrow) SafepointPageManager();
LOGF_CHECK(safepointPageManager_ != nullptr) << "new safepointPageManager failed";
safepointPageManager_->Init();
#endif
SetSuspensionMutatorCount(0);
}
MutatorManager& MutatorManager::Instance() noexcept
{
return BaseRuntime::GetInstance()->GetMutatorManager();
}
void MutatorManager::AcquireMutatorManagementWLock()
{
uint64_t start = TimeUtil::NanoSeconds();
bool acquired = TryAcquireMutatorManagementWLock();
while (!acquired) {
TimeUtil::SleepForNano(WAIT_LOCK_INTERVAL);
acquired = TryAcquireMutatorManagementWLock();
uint64_t now = TimeUtil::NanoSeconds();
if (!acquired && ((now - start) / SECOND_TO_NANO_SECOND > WAIT_LOCK_TIMEOUT)) {
LOG_COMMON(FATAL) << "Wait mutator list lock timeout";
UNREACHABLE_CC();
}
}
}
bool MutatorManager::AcquireMutatorManagementWLockForCpuProfile()
{
uint64_t start = TimeUtil::NanoSeconds();
bool acquired = TryAcquireMutatorManagementWLock();
while (!acquired) {
TimeUtil::SleepForNano(WAIT_LOCK_INTERVAL);
acquired = TryAcquireMutatorManagementWLock();
uint64_t now = TimeUtil::NanoSeconds();
if (!acquired && ((now - start) / SECOND_TO_NANO_SECOND > WAIT_LOCK_TIMEOUT)) {
LOG_COMMON(FATAL) << "Wait mutator list lock timeout";
UNREACHABLE_CC();
}
}
return acquired;
}
void MutatorManager::VisitAllMutators(MutatorVisitor func, bool ignoreFinalizer)
{
{
std::lock_guard<std::mutex> guard(allMutatorListLock_);
for (auto mutator : allMutatorList_) {
func(*mutator);
}
}
if (!ignoreFinalizer) {
Mutator* mutator = Heap::GetHeap().GetFinalizerProcessor().GetMutator();
if (mutator != nullptr) {
func(*mutator);
}
}
}
void MutatorManager::StopTheWorld(bool syncGCPhase, GCPhase phase)
{
#ifndef NDEBUG
bool saferegionEntered = false;
if (!IsGcThread()) {
Mutator* mutator = Mutator::GetMutator();
if (mutator != nullptr) {
saferegionEntered = mutator->EnterSaferegion(true);
}
}
#endif
stwMutex_.lock();
stwTriggered_.store(true);
AcquireMutatorManagementWLock();
#ifndef NDEBUG
saferegionStateChanged_ = saferegionEntered;
#endif
size_t mutatorCount = GetMutatorCount();
if (UNLIKELY_CC(mutatorCount == 0)) {
worldStopped_.store(true, std::memory_order_release);
if (syncGCPhase) { TransitionAllMutatorsToGCPhase(phase); }
return;
}
SetSuspensionMutatorCount(static_cast<uint32_t>(mutatorCount));
DemandSuspensionForStw();
WaitUntilAllStopped();
worldStopped_.store(true, std::memory_order_release);
if (syncGCPhase) { TransitionAllMutatorsToGCPhase(phase); }
}
void MutatorManager::StartTheWorld() noexcept
{
#ifndef NDEBUG
bool shouldLeaveSaferegion = saferegionStateChanged_;
#endif
stwTriggered_.store(false);
worldStopped_.store(false, std::memory_order_release);
CancelSuspensionAfterStw();
SetSuspensionMutatorCount(0);
#if defined(_WIN64) || defined(__APPLE__)
WakeAllMutators();
#else
(void)Futex(GetStwFutexWord(), FUTEX_WAKE, INT_MAX);
#endif
MutatorManagementWUnlock();
stwMutex_.unlock();
#ifndef NDEBUG
if (!IsGcThread()) {
Mutator* mutator = Mutator::GetMutator();
if (mutator != nullptr && shouldLeaveSaferegion) {
(void)mutator->LeaveSaferegion();
}
}
#endif
}
void MutatorManager::WaitUntilAllStopped()
{
uint64_t beginTime = TimeUtil::MilliSeconds();
std::list<Mutator*> unstoppedMutators;
auto func = [&unstoppedMutators](Mutator& mutator) {
if ((!mutator.InSaferegion())) {
unstoppedMutators.emplace_back(&mutator);
}
};
VisitAllMutators(func);
size_t remainMutatorsSize = unstoppedMutators.size();
if (remainMutatorsSize == 0) {
return;
}
int timeoutTimes = 0;
while (true) {
for (auto it = unstoppedMutators.begin(); it != unstoppedMutators.end();) {
Mutator* mutator = *it;
if (mutator->InSaferegion()) {
it = unstoppedMutators.erase(it);
} else {
++it;
}
}
if (unstoppedMutators.size() == 0) {
return;
}
if (UNLIKELY_CC(common::g_enableGCTimeoutCheck && TimeUtil::MilliSeconds() - beginTime >
(((remainMutatorsSize / STW_TIMEOUTS_THREADS_BASE_COUNT) * STW_TIMEOUTS_BASE_MS) + STW_TIMEOUTS_BASE_MS))) {
timeoutTimes++;
beginTime = TimeUtil::MilliSeconds();
DumpMutators(timeoutTimes);
}
(void)sched_yield();
}
}
void MutatorManager::EnsurePhaseTransition(GCPhase phase, std::list<Mutator*> &undoneMutators)
{
while (undoneMutators.size() > 0) {
for (auto it = undoneMutators.begin(); it != undoneMutators.end();) {
Mutator* mutator = *it;
if (mutator->GetMutatorPhase() == phase && mutator->FinishedTransition()) {
it = undoneMutators.erase(it);
continue;
}
if (mutator->InSaferegion() && mutator->TransitionGCPhase(false)) {
it = undoneMutators.erase(it);
continue;
}
++it;
}
}
}
void MutatorManager::TransitionAllMutatorsToGCPhase(GCPhase phase)
{
bool worldStopped = WorldStopped();
if (!worldStopped) {
AcquireMutatorManagementWLock();
}
GCPhase prevPhase = Heap::GetHeap().GetGCPhase();
Heap::GetHeap().InstallBarrier(phase);
Heap::GetHeap().SetGCPhase(phase);
VLOG(DEBUG, "transition gc phase: %s(%u) -> %s(%u)",
Collector::GetGCPhaseName(prevPhase), prevPhase, Collector::GetGCPhaseName(phase), phase);
std::list<Mutator*> undoneMutators;
VisitAllMutators([&undoneMutators, phase](Mutator& mutator) {
mutator.SetSuspensionFlag(Mutator::SuspensionType::SUSPENSION_FOR_GC_PHASE);
mutator.SetSafepointActive(true);
undoneMutators.push_back(&mutator);
});
EnsurePhaseTransition(phase, undoneMutators);
if (!worldStopped) {
MutatorManagementWUnlock();
}
}
void MutatorManager::EnsureCpuProfileFinish(std::list<Mutator*> &undoneMutators)
{
while (undoneMutators.size() > 0) {
for (auto it = undoneMutators.begin(); it != undoneMutators.end();) {
Mutator* mutator = *it;
if (mutator->FinishedCpuProfile()) {
it = undoneMutators.erase(it);
continue;
}
if (mutator->InSaferegion() && mutator->TransitionToCpuProfile(false)) {
it = undoneMutators.erase(it);
continue;
}
++it;
}
}
}
void MutatorManager::DumpMutators([[maybe_unused]] uint32_t timeoutTimes)
{
#ifndef CMC_LCOV_EXCL
constexpr size_t bufferSize = 4096;
char buf[bufferSize];
int index = 0;
size_t visitedCount = 0;
size_t visitedSaferegion = 0;
int firstNotStoppedTid = -1;
index += sprintf_s(buf, sizeof(buf), "not stopped: ");
LOGF_CHECK(index != -1) << "Dump mutators state failed";
size_t mutatorCount = 0;
VisitAllMutators([&](const Mutator& mut) {
mutatorCount++;
#ifndef NDEBUG
mut.DumpMutator();
#endif
if (!mut.InSaferegion()) {
if (firstNotStoppedTid == -1) {
firstNotStoppedTid = static_cast<int>(mut.GetTid());
}
int ret = sprintf_s(buf + index, sizeof(buf) - index, "%u ", mut.GetTid());
LOGF_CHECK(ret != -1) << "Dump mutators state failed";
index += ret;
} else {
++visitedSaferegion;
}
++visitedCount;
});
LOG_COMMON(ERROR) << "MutatorList size: " << mutatorCount;
LOGF_CHECK(sprintf_s(buf + index, sizeof(buf) - index, ", total: %u, visited: %zu/%zu",
GetSuspensionMutatorCount(), visitedSaferegion, visitedCount) != -1) <<
"Dump mutators state failed";
LOGF_CHECK(timeoutTimes <= MAX_TIMEOUT_TIMES) << "Waiting mutators entering saferegion timeout status info:" << buf;
LOG_COMMON(ERROR) << "STW status info: " << buf;
#endif
}
#if defined(GCINFO_DEBUG) && GCINFO_DEBUG
void MutatorManager::DumpForDebug()
{
size_t count = 0;
auto func = [&count](Mutator& mutator) {
mutator.DumpMutator();
count++;
};
VisitAllMutators(func);
LOG_COMMON(INFO) << "MutatorList size : " << count;
}
void MutatorManager::DumpAllGcInfos()
{
auto func = [](Mutator& mutator) { mutator.DumpGCInfos(); };
VisitAllMutators(func);
}
#endif
}