1 /* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef COMMON_COMPONENTS_HEAP_ALLOCATOR_FREE_REGION_MANAGER_H 17 #define COMMON_COMPONENTS_HEAP_ALLOCATOR_FREE_REGION_MANAGER_H 18 19 #include "common_components/heap/allocator/treap.h" 20 #include "common_components/heap/allocator/region_desc.h" 21 #include "common_components/common/scoped_object_access.h" 22 23 namespace common { 24 class RegionManager; 25 26 // This class is and should be accessed only for region allocation. we do not rely on it to check region status. 27 class FreeRegionManager { 28 public: FreeRegionManager(RegionManager & manager)29 explicit FreeRegionManager(RegionManager& manager) : regionManager_(manager) {} 30 ~FreeRegionManager()31 virtual ~FreeRegionManager() 32 { 33 dirtyUnitTree_.Fini(); 34 releasedUnitTree_.Fini(); 35 } 36 Initialize(uint32_t regionCnt)37 void Initialize(uint32_t regionCnt) 38 { 39 releasedUnitTree_.Init(regionCnt); 40 dirtyUnitTree_.Init(regionCnt); 41 } 42 TakeRegion(size_t num,RegionDesc::UnitRole uclass,bool expectPhysicalMem)43 RegionDesc* TakeRegion(size_t num, RegionDesc::UnitRole uclass, bool expectPhysicalMem) 44 { 45 uint32_t idx = 0; 46 bool tryDirtyTree = true; 47 bool tryReleasedTree = true; 48 49 // try as hard as we can to take free regions for allocation. 50 while (tryDirtyTree || tryReleasedTree) { 51 // first try to get a dirty region. 52 if (tryDirtyTree && dirtyUnitTreeMutex_.try_lock()) { 53 if (dirtyUnitTree_.TakeUnits(num, idx)) { 54 DLOG(REGION, "c-tree %p alloc dirty units[%u+%u, %u) @[0x%zx, 0x%zx), %u dirty-units left", 55 &dirtyUnitTree_, idx, num, idx + num, RegionDesc::GetUnitAddress(idx), 56 RegionDesc::GetUnitAddress(idx + num), dirtyUnitTree_.GetTotalCount()); 57 58 // it makes sense to slow down allocation by clearing region memory. 59 RegionDesc::ClearUnits(idx, num); 60 RegionDesc* region = RegionDesc::InitRegion(idx, num, uclass); 61 dirtyUnitTreeMutex_.unlock(); 62 return region; 63 } 64 tryDirtyTree = false; // once we fail to take units, stop trying. 65 dirtyUnitTreeMutex_.unlock(); 66 } 67 68 // then try to get a released region. 69 if (tryReleasedTree && releasedUnitTreeMutex_.try_lock()) { 70 if (releasedUnitTree_.TakeUnits(num, idx)) { 71 #ifdef _WIN64 72 MemoryMap::CommitMemory( 73 reinterpret_cast<void*>(RegionDesc::GetUnitAddress(idx)), num * RegionDesc::UNIT_SIZE); 74 #endif 75 DLOG(REGION, "c-tree %p alloc released units%u+%u @0x%zx+%zu, %u released-units left", 76 &releasedUnitTree_, idx, num, RegionDesc::GetUnitAddress(idx), num * RegionDesc::UNIT_SIZE, 77 releasedUnitTree_.GetTotalCount()); 78 RegionDesc* region = RegionDesc::InitRegion(idx, num, uclass); 79 releasedUnitTreeMutex_.unlock(); 80 PrehandleReleasedUnit(expectPhysicalMem, idx, num); 81 return region; 82 } 83 tryReleasedTree = false; // once we fail to take units, stop trying. 84 releasedUnitTreeMutex_.unlock(); 85 } 86 } 87 88 return nullptr; 89 } 90 91 // add units [idx, idx + num) AddGarbageUnits(uint32_t idx,uint32_t num)92 void AddGarbageUnits(uint32_t idx, uint32_t num) 93 { 94 std::lock_guard<std::mutex> lg(dirtyUnitTreeMutex_); 95 if (UNLIKELY_CC(!dirtyUnitTree_.MergeInsert(idx, num, true))) { 96 LOG_COMMON(FATAL) << "tid " << GetTid() << ": failed to add dirty units [" << 97 idx << "+" << num << ", " << (idx + num) << ")"; 98 } 99 } 100 AddReleaseUnits(uint32_t idx,uint32_t num)101 void AddReleaseUnits(uint32_t idx, uint32_t num) 102 { 103 std::lock_guard<std::mutex> lg(releasedUnitTreeMutex_); 104 if (UNLIKELY_CC(!releasedUnitTree_.MergeInsert(idx, num, true))) { 105 LOG_COMMON(FATAL) << "tid %d: failed to add release units [" << 106 idx << "+" << num << ", " << (idx + num) << ")"; 107 } 108 } 109 GetDirtyUnitCount()110 uint32_t GetDirtyUnitCount() const { return dirtyUnitTree_.GetTotalCount(); } GetReleasedUnitCount()111 uint32_t GetReleasedUnitCount() const { return releasedUnitTree_.GetTotalCount(); } 112 113 #ifndef NDEBUG DumpReleasedUnitTree()114 void DumpReleasedUnitTree() const { releasedUnitTree_.DumpTree("released-unit tree"); } DumpDirtyUnitTree()115 void DumpDirtyUnitTree() const { dirtyUnitTree_.DumpTree("dirty-unit tree"); } 116 #endif 117 118 size_t CalculateBytesToRelease() const; 119 size_t ReleaseGarbageRegions(size_t targetCachedSize); 120 121 private: PrehandleReleasedUnit(bool expectPhysicalMem,size_t idx,size_t num)122 inline void PrehandleReleasedUnit(bool expectPhysicalMem, size_t idx, size_t num) const 123 { 124 if (expectPhysicalMem) { 125 RegionDesc::ClearUnits(idx, num); 126 } 127 } 128 RegionManager& regionManager_; 129 130 // physical pages of released units are probably released and they are prepared for allocation. 131 std::mutex releasedUnitTreeMutex_; 132 Treap releasedUnitTree_; 133 134 // dirty units are neither cleared nor released, thus must be zeroed explicitly for allocation. 135 std::mutex dirtyUnitTreeMutex_; 136 Treap dirtyUnitTree_; 137 }; 138 } // namespace common 139 #endif // COMMON_COMPONENTS_HEAP_ALLOCATOR_FREE_REGION_MANAGER_H 140