• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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