#ifndef MRT_REGION_MANAGER_H
#define MRT_REGION_MANAGER_H
#include <list>
#include <map>
#include <set>
#include <thread>
#include <vector>
#include "AllocBuffer.h"
#include "Allocator.h"
#include "Common/RunType.h"
#include "FreeRegionManager.h"
#include "Heap/GcThreadPool.h"
#include "RegionList.h"
#include "securec.h"
#include "SlotList.h"
#include "Sync/Sync.h"
namespace MapleRuntime {
class CopyCollector;
class CompactCollector;
struct FreePinnedSlotLists {
static constexpr size_t ATOMIC_OBJECT_SIZE = 16;
static constexpr size_t SYNC_OBJECT_SIZE = CJFuture::SYNC_OBJECT_SIZE;
SlotList freeAtomicSlotList;
SlotList freeSyncSlotList;
uintptr_t PopFront(size_t size)
{
switch (size) {
case ATOMIC_OBJECT_SIZE:
return freeAtomicSlotList.PopFront(size);
case SYNC_OBJECT_SIZE:
return freeSyncSlotList.PopFront(size);
default:
return 0;
}
}
void PushFront(BaseObject* slot)
{
size_t size = slot->GetSize();
switch (size) {
case ATOMIC_OBJECT_SIZE:
freeAtomicSlotList.PushFront(slot);
break;
case SYNC_OBJECT_SIZE:
freeSyncSlotList.PushFront(slot);
break;
default:
return;
}
}
void Clear()
{
freeAtomicSlotList.Clear();
freeSyncSlotList.Clear();
}
};
class RegionManager {
public:
1. region info for each region, part of heap metadata
2. region space for allocation, i.e., the heap
*/
static size_t GetHeapMemorySize(size_t heapSize)
{
size_t unitNum = GetHeapUnitCount(heapSize);
size_t metadataSize = GetMetadataSize(unitNum);
size_t totalSize = metadataSize + RoundUp<size_t>(heapSize, RegionInfo::UNIT_SIZE);
return totalSize;
}
static size_t GetHeapUnitCount(size_t heapSize)
{
heapSize = RoundUp<size_t>(heapSize, RegionInfo::UNIT_SIZE);
size_t unitNum = heapSize / RegionInfo::UNIT_SIZE;
return unitNum;
}
static size_t GetMetadataSize(size_t num)
{
size_t metadataSize = num * sizeof(RegionInfo);
return RoundUp<size_t>(metadataSize, MapleRuntime::MRT_PAGE_SIZE);
}
#if defined(__EULER__)
void SetCacheRatio(double minSize, double maxSize, double defaultParam);
#endif
void Initialize(size_t regionNum, uintptr_t regionInfoStart);
RegionManager()
: freeRegionManager(*this), tlRegionList("thread local regions"), recentFullRegionList("recent full regions"),
fullTraceRegions("full trace regions"), fromRegionList("from regions"),
ghostFromRegionList("ghost from regions"), unmovableFromRegionList("escaped from regions"),
garbageRegionList("garbage regions"), recentPinnedRegionList("recent pinned regions"),
oldPinnedRegionList("old pinned regions"), rawPointerPinnedRegionList("raw pointer pinned regions"),
oldLargeRegionList("old large regions"), recentLargeRegionList("recent large regions"),
largeTraceRegions("large trace regions")
{}
RegionManager(const RegionManager&) = delete;
RegionManager& operator=(const RegionManager&) = delete;
RegionInfo* AllocateThreadLocalRegion(bool expectPhysicalMem = false);
void ForwardFromRegions(GCThreadPool* threadPool);
void ForwardFromRegions();
void ForwardRegion(RegionInfo* region);
void CompactRegion(RegionInfo* region);
void CompactRegion(RegionInfo* region, RegionInfo* toRegion1);
void ExemptFromRegion(RegionInfo* region);
#if defined(GCINFO_DEBUG) && GCINFO_DEBUG
void DumpRegionInfo() const;
#endif
void DumpRegionStats(const char* msg, bool dumpToError = false) const;
uintptr_t GetInactiveZone() const { return inactiveZone; }
#if defined(__EULER__)
double GetCacheRatio() const { return cacheRatio; }
#endif
uintptr_t GetRegionHeapStart() const { return regionHeapStart; }
~RegionManager() = default;
RegionInfo* TakeRegion(size_t num, RegionInfo::UnitRole, bool expectPhysicalMem = false);
uintptr_t AllocPinnedFromFreeList(size_t size);
uintptr_t AllocPinned(size_t size)
{
uintptr_t addr = 0;
std::mutex& regionListMutex = recentPinnedRegionList.GetListMutex();
{
ScopedEnterSaferegion enterSaferegion(true);
regionListMutex.lock();
}
RegionInfo* headRegion = recentPinnedRegionList.GetHeadRegion();
if (headRegion != nullptr) {
addr = headRegion->Alloc(size);
}
if (addr == 0) {
addr = AllocPinnedFromFreeList(size);
}
if (addr == 0) {
size_t needUnitCount = maxUnitCountPerRegion;
#if defined(__EULER__)
needUnitCount = maxUnitCountPerPinnedRegion;
#endif
RegionInfo* region = TakeRegion(needUnitCount, RegionInfo::UnitRole::SMALL_SIZED_UNITS);
if (region == nullptr) {
regionListMutex.unlock();
return 0;
}
DLOG(REGION, "alloc pinned region @[0x%zx+%zu, 0x%zx) unit idx %zu type %u", region->GetRegionStart(),
region->GetRegionAllocatedSize(), region->GetRegionEnd(), region->GetUnitIdx(),
region->GetRegionType());
GCPhase phase = Heap::GetHeap().GetCollector().GetGCPhase();
if (phase == GC_PHASE_TRACE || phase == GC_PHASE_CLEAR_SATB_BUFFER) {
region->SetTraceRegionFlag(1);
}
recentPinnedRegionList.PrependRegionLocked(region, RegionInfo::RegionType::RECENT_PINNED_REGION);
addr = region->Alloc(size);
}
DLOG(ALLOC, "alloc pinned obj 0x%zx(%zu)", addr, size);
regionListMutex.unlock();
return addr;
}
uintptr_t AllocLarge(size_t size)
{
size_t regionCount = (size + RegionInfo::UNIT_SIZE - 1) / RegionInfo::UNIT_SIZE;
RegionInfo* region = TakeRegion(regionCount, RegionInfo::UnitRole::LARGE_SIZED_UNITS);
if (region == nullptr) {
return 0;
}
DLOG(REGION, "alloc large region @[0x%zx+%zu, 0x%zx) unit idx %zu type %u", region->GetRegionStart(),
region->GetRegionSize(), region->GetRegionEnd(), region->GetUnitIdx(), region->GetRegionType());
uintptr_t addr = region->Alloc(size);
GCPhase phase = Heap::GetHeap().GetCollector().GetGCPhase();
bool shouldSetTraceRegion = (phase == GC_PHASE_TRACE || phase == GC_PHASE_CLEAR_SATB_BUFFER);
if (largeTraceRegions.TryPrependRegion(region, RegionInfo::RegionType::RECENT_LARGE_REGION)) {
if (shouldSetTraceRegion) {
region->SetTraceRegionFlag(1);
}
} else {
recentLargeRegionList.PrependRegion(region, RegionInfo::RegionType::RECENT_LARGE_REGION);
region->SetTraceRegionFlag(0);
}
return addr;
}
void EnlistFullThreadLocalRegion(RegionInfo* region) noexcept
{
MRT_ASSERT(region->IsThreadLocalRegion(), "unexpected region type");
if (region->IsTraceRegion()) {
if (!fullTraceRegions.TryPrependRegion(region, RegionInfo::RegionType::RECENT_FULL_REGION)) {
recentFullRegionList.PrependRegion(region, RegionInfo::RegionType::RECENT_FULL_REGION);
region->SetTraceRegionFlag(0);
}
return;
}
recentFullRegionList.PrependRegion(region, RegionInfo::RegionType::RECENT_FULL_REGION);
}
void RemoveThreadLocalRegion(RegionInfo* region) noexcept
{
MRT_ASSERT(region->IsThreadLocalRegion(), "unexpected region type");
tlRegionList.DeleteRegion(region);
}
void RestoreToSpaceStateWords();
void CountLiveObject(const BaseObject* obj);
void AssembleSmallGarbageCandidates();
void AssembleLargeGarbageCandidates();
void AssemblePinnedGarbageCandidates(bool collectAll);
void MergeRawPointerPinnedRegions()
{
oldPinnedRegionList.MergeRegionList(rawPointerPinnedRegionList, RegionInfo::RegionType::FULL_PINNED_REGION);
}
void CollectFromSpaceGarbage()
{
RegionInfo* region = fromRegionList.TakeHeadRegion();
while (region != nullptr) {
ReclaimRegion(region);
region = fromRegionList.TakeHeadRegion();
}
}
size_t GetThreadLocalRegionSize() const
{
return maxUnitCountPerRegion * RegionInfo::UNIT_SIZE;
}
size_t CollectRegion(RegionInfo* region)
{
DLOG(REGION, "collect region %p@[%#zx+%zu, %#zx) type %u", region, region->GetRegionStart(),
region->GetLiveByteCount(), region->GetRegionEnd(), region->GetRegionType());
region->LockWriteRegion();
ReclaimRegion(region);
region->UnlockWriteRegion();
if (region->IsLargeRegion()) {
return region->GetRegionSize();
} else {
return region->GetRegionSize() - region->GetLiveByteCount();
}
}
void AddRawPointerObject(BaseObject* obj)
{
RegionInfo* region = RegionInfo::GetRegionInfoAt(reinterpret_cast<MAddress>(obj));
region->IncRawPointerObjectCount();
if (region->IsFromRegion() && fromRegionList.TryDeleteRegion(region, RegionInfo::RegionType::FROM_REGION,
RegionInfo::RegionType::RAW_POINTER_PINNED_REGION)) {
GCPhase phase = Heap::GetHeap().GetGCPhase();
CHECK(phase != GCPhase::GC_PHASE_FORWARD && phase != GCPhase::GC_PHASE_PREFORWARD);
if (phase == GCPhase::GC_PHASE_POST_TRACE) {
region->ClearGhostRegionBit();
}
rawPointerPinnedRegionList.PrependRegion(region, RegionInfo::RegionType::RAW_POINTER_PINNED_REGION);
} else {
CHECK(region->GetRegionType() != RegionInfo::RegionType::LONE_FROM_REGION);
}
}
void RemoveRawPointerObject(BaseObject* obj)
{
RegionInfo* region = RegionInfo::GetRegionInfoAt(reinterpret_cast<MAddress>(obj));
region->DecRawPointerObjectCount();
}
void ReclaimRegion(RegionInfo* region);
size_t ReleaseRegion(RegionInfo* region);
void ReclaimGarbageRegions()
{
RegionInfo* garbage = garbageRegionList.TakeHeadRegion();
while (garbage != nullptr) {
ReclaimRegion(garbage);
garbage = garbageRegionList.TakeHeadRegion();
}
}
size_t CollectLargeGarbage();
size_t CollectPinnedGarbage();
size_t CollectFreePinnedSlots(RegionInfo* region);
size_t ReleaseGarbageRegions(size_t targetSize) { return freeRegionManager.ReleaseGarbageRegions(targetSize); }
size_t ExemptFromRegions();
void ReassembleFromSpace();
void ForEachObjUnsafe(const std::function<void(BaseObject*)>& visitor) const;
void ForEachObjSafe(const std::function<void(BaseObject*)>& visitor) const;
size_t GetUsedRegionSize() const { return GetUsedUnitCount() * RegionInfo::UNIT_SIZE; }
size_t GetRecentAllocatedSize() const
{
return recentFullRegionList.GetAllocatedSize() + recentLargeRegionList.GetAllocatedSize() +
recentPinnedRegionList.GetAllocatedSize();
}
size_t GetSurvivedSize() const
{
return fromRegionList.GetAllocatedSize() + oldPinnedRegionList.GetAllocatedSize() +
oldLargeRegionList.GetAllocatedSize();
}
size_t GetUsedUnitCount() const
{
return
fromRegionList.GetUnitCount() + unmovableFromRegionList.GetUnitCount() +
recentFullRegionList.GetUnitCount() + oldLargeRegionList.GetUnitCount() +
recentLargeRegionList.GetUnitCount() + oldPinnedRegionList.GetUnitCount() +
recentPinnedRegionList.GetUnitCount() + rawPointerPinnedRegionList.GetUnitCount() +
largeTraceRegions.GetUnitCount() + fullTraceRegions.GetUnitCount() +
tlRegionList.GetUnitCount();
}
size_t GetDirtyUnitCount() const { return freeRegionManager.GetDirtyUnitCount(); }
size_t GetInactiveUnitCount() const { return (regionHeapEnd - inactiveZone) / RegionInfo::UNIT_SIZE; }
size_t GetActiveUnitCount() const { return (inactiveZone - regionHeapStart) / RegionInfo::UNIT_SIZE; }
inline size_t GetLargeObjectSize() const
{
return oldLargeRegionList.GetAllocatedSize() + recentLargeRegionList.GetAllocatedSize();
}
size_t GetAllocatedSize() const
{
size_t threadLocalSize = 0;
AllocBufferVisitor visitor = [&threadLocalSize](AllocBuffer& regionBuffer) {
RegionInfo* region = regionBuffer.GetRegion();
if (UNLIKELY(region == RegionInfo::NullRegion())) {
return;
}
threadLocalSize += region->GetRegionAllocatedSize();
};
Heap::GetHeap().GetAllocator().VisitAllocBuffers(visitor);
return fromRegionList.GetAllocatedSize() + unmovableFromRegionList.GetAllocatedSize() +
recentFullRegionList.GetAllocatedSize() + oldLargeRegionList.GetAllocatedSize() +
recentLargeRegionList.GetAllocatedSize() + oldPinnedRegionList.GetAllocatedSize() +
recentPinnedRegionList.GetAllocatedSize() + rawPointerPinnedRegionList.GetAllocatedSize() +
largeTraceRegions.GetAllocatedSize() + fullTraceRegions.GetAllocatedSize() +
threadLocalSize;
}
inline size_t GetFromSpaceSize() const { return fromRegionList.GetAllocatedSize(); }
inline size_t GetPinnedSpaceSize() const
{
return oldPinnedRegionList.GetAllocatedSize() + recentPinnedRegionList.GetAllocatedSize();
}
size_t GetLargeObjectThreshold() const { return largeObjectThreshold; }
void ClearFreePinnedSlots() { freePinnedSlotLists.Clear(); }
void RequestForRegion(size_t size);
void MergeRawPointerRegions(RegionList& smallSizeRegionList, RegionList& largeSizeRegionList)
{
recentFullRegionList.MergeRegionList(smallSizeRegionList, RegionInfo::RegionType::RECENT_FULL_REGION);
recentLargeRegionList.MergeRegionList(largeSizeRegionList, RegionInfo::RegionType::RECENT_LARGE_REGION);
}
void SetMaxUnitCountForRegion();
void SetMaxUnitCountForPinnedRegion();
void SetLargeObjectThreshold();
void SetGarbageThreshold();
void HandleTraceRegions()
{
fullTraceRegions.DeactivateRegionCache();
recentFullRegionList.MergeRegionList(fullTraceRegions, RegionInfo::RegionType::RECENT_FULL_REGION);
largeTraceRegions.DeactivateRegionCache();
recentLargeRegionList.MergeRegionList(largeTraceRegions, RegionInfo::RegionType::RECENT_LARGE_REGION);
tlRegionList.ClearTraceRegionFlag();
recentPinnedRegionList.ClearTraceRegionFlag();
oldPinnedRegionList.ClearTraceRegionFlag();
}
void PrepareTrace()
{
fullTraceRegions.ActivateRegionCache();
largeTraceRegions.ActivateRegionCache();
}
bool RouteOrCompactRegionImpl(RegionInfo* region);
BaseObject* RouteObject(BaseObject* fromObj, RegionInfo* fromRegionInfo)
{
if (RouteRegion(fromRegionInfo) || fromRegionInfo->IsCompacted()) {
BaseObject* toAddr = fromRegionInfo->GetRoute(fromObj);
return toAddr;
}
return nullptr;
}
BaseObject* RouteObject(BaseObject* fromObj)
{
RegionInfo* fromRegionInfo = RegionInfo::GetGhostFromRegionAt(reinterpret_cast<MAddress>(fromObj));
if (fromRegionInfo == nullptr) {
return nullptr;
}
if (RouteRegion(fromRegionInfo) || fromRegionInfo->IsCompacted()) {
BaseObject* toAddr = fromRegionInfo->GetRoute(fromObj);
return toAddr;
}
return nullptr;
}
bool RouteRegion(RegionInfo* fromRegionInfo)
{
CHECK(fromRegionInfo->IsGhostFromRegion());
do {
RegionInfo::RouteState oldState = fromRegionInfo->GetRouteState();
if (oldState == RegionInfo::RouteState::ROUTED || oldState == RegionInfo::RouteState::FORWARDED) {
return true;
}
if (oldState == RegionInfo::RouteState::COMPACTED) {
return false;
}
if (oldState == RegionInfo::RouteState::ROUTING) {
sched_yield();
continue;
}
CHECK(oldState == MapleRuntime::RegionInfo::FORWARDABLE);
if (fromRegionInfo->TryLockRouting(oldState)) {
if (RouteOrCompactRegionImpl(fromRegionInfo)) {
fromRegionInfo->SetRouteState(RegionInfo::RouteState::ROUTED);
return true;
} else {
fromRegionInfo->SetRouteState(RegionInfo::RouteState::COMPACTED);
return false;
}
}
} while (true);
}
void PrepareFromRegionList()
{
ghostFromRegionList.VisitAllGhostRegions([](RegionInfo* region) {
DLOG(REGION, "visit ghost from region %p@[%#zx, %#zx)", region, region->GetRegionStart(),
region->GetRegionEnd());
region->DispelGhostFromRegion();
});
fromRegionList.VisitAllRegions([](RegionInfo* region) {
DLOG(REGION, "visit from region %p@[%#zx+%zu, %#zx)", region, region->GetRegionStart(),
region->GetLiveByteCount(), region->GetRegionEnd());
region->PrepareForwardableRegion();
});
fromRegionList.CopyListTo(ghostFromRegionList);
}
void ClearAllLiveInfo()
{
ClearLiveInfo(tlRegionList);
ClearLiveInfo(recentFullRegionList);
ClearLiveInfo(fullTraceRegions);
ClearLiveInfo(unmovableFromRegionList);
ClearLiveInfo(recentPinnedRegionList);
ClearLiveInfo(oldPinnedRegionList);
ClearLiveInfo(rawPointerPinnedRegionList);
ClearLiveInfo(oldLargeRegionList);
ClearLiveInfo(recentLargeRegionList);
ClearLiveInfo(largeTraceRegions);
}
private:
static const size_t MAX_UNIT_COUNT_PER_REGION;
static const size_t HUGE_PAGE;
inline void CheckRegionWhetherCreatedInFixPhase(RegionInfo* region);
inline void TagHugePage(RegionInfo* region, size_t num) const;
inline void UntagHugePage(RegionInfo* region, size_t num) const;
void ClearLiveInfo(RegionList& list)
{
RegionList tmp("temp region list");
list.CopyListTo(tmp);
tmp.VisitAllRegions([](RegionInfo* region) { region->ClearLiveInfo(); });
}
FreeRegionManager freeRegionManager;
RegionList tlRegionList;
RegionList recentFullRegionList;
RegionCache fullTraceRegions;
RegionList fromRegionList;
RegionList ghostFromRegionList;
RegionList unmovableFromRegionList;
RegionList garbageRegionList;
RegionList recentPinnedRegionList;
RegionList oldPinnedRegionList;
RegionList rawPointerPinnedRegionList;
RegionList oldLargeRegionList;
RegionList recentLargeRegionList;
RegionCache largeTraceRegions;
uintptr_t regionInfoStart = 0;
uintptr_t regionHeapStart = 0;
uintptr_t regionHeapEnd = 0;
std::atomic<uint64_t> prevRegionAllocTime = { 0 };
std::atomic<uintptr_t> inactiveZone = { 0 };
size_t maxUnitCountPerRegion = MAX_UNIT_COUNT_PER_REGION;
size_t maxUnitCountPerPinnedRegion = maxUnitCountPerRegion;
size_t largeObjectThreshold;
double fromSpaceGarbageThreshold = 0.5;
double exemptedRegionThreshold;
#if defined(__EULER__)
double cacheRatio;
#endif
std::mutex freePinnedSlotListMutex;
FreePinnedSlotLists freePinnedSlotLists;
};
}
#endif