#ifndef MRT_FREE_REGION_MANAGER_H
#define MRT_FREE_REGION_MANAGER_H
#include "CartesianTree.h"
#include "RegionInfo.h"
#include "Common/ScopedObjectAccess.h"
namespace MapleRuntime {
class RegionManager;
class FreeRegionManager {
using UnitIndex = CartesianTree::Index;
using UnitCount = CartesianTree::Count;
public:
explicit FreeRegionManager(RegionManager& manager) : regionManager(manager) {}
virtual ~FreeRegionManager()
{
dirtyUnitTree.Fini();
releasedUnitTree.Fini();
}
void Initialize(UnitCount regionCnt)
{
releasedUnitTree.Init(regionCnt);
dirtyUnitTree.Init(regionCnt);
}
RegionInfo* TakeRegion(size_t num, RegionInfo::UnitRole uclass, bool expectPhysicalMem)
{
UnitIndex idx = 0;
bool tryDirtyTree = true;
bool tryReleasedTree = true;
while (tryDirtyTree || tryReleasedTree) {
if (tryDirtyTree && dirtyUnitTreeMutex.try_lock()) {
bool dirtyOk = dirtyUnitTree.TakeUnitsLowAddr(num, idx);
if (dirtyOk) {
DLOG(REGION, "c-tree %p alloc dirty units[%u+%u, %u) @[0x%zx, 0x%zx), %u dirty-units left",
&dirtyUnitTree, idx, num, idx + num, RegionInfo::GetUnitAddress(idx),
RegionInfo::GetUnitAddress(idx + num), dirtyUnitTree.GetTotalCount());
RegionInfo::ClearUnits(idx, num);
RegionInfo* region = RegionInfo::InitRegion(idx, num, uclass);
dirtyUnitTreeMutex.unlock();
return region;
}
tryDirtyTree = false;
dirtyUnitTreeMutex.unlock();
}
if (tryReleasedTree && releasedUnitTreeMutex.try_lock()) {
bool releasedOk = releasedUnitTree.TakeUnitsLowAddr(num, idx);
if (releasedOk) {
#ifdef _WIN64
MemMap::CommitMemory(
reinterpret_cast<void*>(RegionInfo::GetUnitAddress(idx)), num * RegionInfo::UNIT_SIZE);
#endif
DLOG(REGION, "c-tree %p alloc released units[%u+%u, %u) @[0x%zx, 0x%zx), %u released-units left",
&releasedUnitTree, idx, num, idx + num, RegionInfo::GetUnitAddress(idx),
RegionInfo::GetUnitAddress(idx + num), releasedUnitTree.GetTotalCount());
RegionInfo* region = RegionInfo::InitRegion(idx, num, uclass);
releasedUnitTreeMutex.unlock();
PrehandleReleasedUnit(expectPhysicalMem, idx, num);
return region;
}
tryReleasedTree = false;
releasedUnitTreeMutex.unlock();
}
ScopedEnterSaferegion enterSaferegion(true);
}
return nullptr;
}
void AddGarbageUnits(UnitIndex idx, UnitCount num)
{
ScopedEnterSaferegion enterSaferegion(true);
std::lock_guard<std::mutex> lg(dirtyUnitTreeMutex);
if (UNLIKELY(!dirtyUnitTree.MergeInsert(idx, num, true))) {
LOG(RTLOG_FATAL, "tid %d: failed to add dirty units [%u+%u, %u)", GetTid(), idx, num, idx + num);
}
}
void AddReleaseUnits(UnitIndex idx, UnitCount num)
{
ScopedEnterSaferegion enterSaferegion(true);
std::lock_guard<std::mutex> lg(releasedUnitTreeMutex);
if (UNLIKELY(!releasedUnitTree.MergeInsert(idx, num, true))) {
LOG(RTLOG_FATAL, "tid %d: failed to add release units [%u+%u, %u)", GetTid(), idx, num, idx + num);
}
}
UnitCount GetDirtyUnitCount() const
{
std::lock_guard<std::mutex> lg(dirtyUnitTreeMutex);
return dirtyUnitTree.GetTotalCount();
}
UnitCount GetReleasedUnitCount() const
{
std::lock_guard<std::mutex> lg(releasedUnitTreeMutex);
return releasedUnitTree.GetTotalCount();
}
UnitCount GetReleasedMaxBlock() const
{
std::lock_guard<std::mutex> lg(releasedUnitTreeMutex);
const auto* r = releasedUnitTree.RootNode();
return r ? r->GetCount() : 0;
}
UnitCount GetDirtyMaxBlock() const
{
std::lock_guard<std::mutex> lg(dirtyUnitTreeMutex);
const auto* r = dirtyUnitTree.RootNode();
return r ? r->GetCount() : 0;
}
size_t GetReleasedNodeCount() const
{
std::lock_guard<std::mutex> lg(releasedUnitTreeMutex);
return releasedUnitTree.GetNodeCount();
}
size_t GetDirtyNodeCount() const
{
std::lock_guard<std::mutex> lg(dirtyUnitTreeMutex);
return dirtyUnitTree.GetNodeCount();
}
#if defined(MRT_DEBUG)
void DumpReleasedUnitTree() const { releasedUnitTree.DumpTree("released-unit tree"); }
void DumpDirtyUnitTree() const { dirtyUnitTree.DumpTree("dirty-unit tree"); }
#endif
size_t CalculateBytesToRelease() const;
size_t ReleaseGarbageRegions(size_t targetCachedSize);
private:
inline void PrehandleReleasedUnit(bool expectPhysicalMem, size_t idx, size_t num) const
{
if (expectPhysicalMem) {
RegionInfo::ClearUnits(idx, num);
}
}
RegionManager& regionManager;
mutable std::mutex releasedUnitTreeMutex;
CartesianTree releasedUnitTree;
mutable std::mutex dirtyUnitTreeMutex;
CartesianTree dirtyUnitTree;
};
}
#endif