• 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_TREAP_H
17 #define COMMON_COMPONENTS_HEAP_ALLOCATOR_TREAP_H
18 
19 #include <cstddef>
20 #include <cstdint>
21 
22 #include "common_components/heap/allocator/local_deque.h"
23 #include "common_components/log/log.h"
24 
25 #ifndef NDEBUG
26 #define CTREE_ASSERT(cond, msg) ASSERT_LOGF(cond, msg)
27 #define CTREE_CHECK_PARENT_AND_LCHILD(n) CheckParentAndLeftChild(n)
28 #define CTREE_CHECK_PARENT_AND_RCHILD(n) CheckParentAndRightChild(n)
29 #else
30 #define CTREE_ASSERT(cond, msg) (void(0))
31 #define CTREE_CHECK_PARENT_AND_LCHILD(n) (void(0))
32 #define CTREE_CHECK_PARENT_AND_RCHILD(n) (void(0))
33 #endif
34 
35 // This is an implementation of a Cartesian tree.
36 // This can be used in arbitrary-sized, free-list allocation algorithm.
37 // The use of this tree and the algorithm is inspired by
38 // R. Jones, A. Hosking, E. Moss. The garbage collection handbook:
39 // the art of automatic memory management. Chapman and Hall/CRC, 2016.
40 // This data structure doesn't guarantee the multi-thread safety, so the external invoker should take some
41 // policy to avoid competition problems.
42 namespace common {
43 class Treap {
44 public:
45     Treap() = default;
46     ~Treap() = default;
47 
Init(size_t memoryBlockCount)48     void Init(size_t memoryBlockCount)
49     {
50         // at most we need this many nodes
51         size_t nodeCount = (memoryBlockCount >> 1) + 1;
52         // calculate how much we need for native allocation
53         // we might need some extra space for some temporaries, so set aside another 7 slots
54         size_t nativeSize = (nodeCount + 7) * AllocUtilRndUp(sizeof(TreapNode), alignof(TreapNode));
55         nodeAllocator_.Init(ALLOCUTIL_PAGE_RND_UP(nativeSize));
56         // calculate how much we need for the deque temporary
57         size_t dequeSize = nodeCount * sizeof(TreapNode*);
58         sud_.Init(ALLOCUTIL_PAGE_RND_UP(dequeSize));
59         traversalSud_.Init(sud_.GetMemMap());
60     }
61 
Fini()62     void Fini() noexcept
63     {
64         ClearTree();
65         sud_.Fini();
66         nodeAllocator_.Fini();
67     }
68 
ClearTree()69     void ClearTree()
70     {
71         Treap::Iterator it(*this);
72         TreapNode* node = it.Next();
73         while (node != nullptr) {
74             DeleteNode(node);
75             node = it.Next();
76         }
77 
78         root_ = nullptr;
79         totalCount_ = 0;
80     }
81 
GetTotalCount()82     uint32_t GetTotalCount() const { return totalCount_; }
83 
IncTotalCount(uint32_t cnt)84     void IncTotalCount(uint32_t cnt) { totalCount_ += cnt; }
85 
DecTotalCount(uint32_t cnt)86     void DecTotalCount(uint32_t cnt)
87     {
88         if (totalCount_ < cnt) { //LCOV_EXCL_BR_LINE
89             LOG_COMMON(FATAL) << "Treap::DecTotalCount() Should not execute here, abort.";
90             UNREACHABLE_CC();
91         }
92         totalCount_ -= cnt;
93     }
94 
95     // insert a node to the tree, if we find connecting nodes, we merge them
96     // (the non-merging insertion is not allowed)
97     // true when insertion succeeded, false otherwise
98     // if [idx, idx + num) clashes with existing node, it fails
99     // if num is 0U, it always fails
MergeInsert(uint32_t idx,uint32_t num,bool refreshRegionDesc)100     bool MergeInsert(uint32_t idx, uint32_t num, bool refreshRegionDesc)
101     {
102         if (root_ == nullptr) {
103             root_ = new (nodeAllocator_.Allocate()) TreapNode(idx, num, refreshRegionDesc);
104             CTREE_ASSERT(root_ != nullptr, "fail to allocate a new node");
105             IncTotalCount(num);
106             return true;
107         }
108 
109         if (num == 0) {
110             return false;
111         }
112 
113         return MergeInsertInternal(idx, num, refreshRegionDesc);
114     }
115 
116     // find a node with a count of at least num, store its index into a
117     // split/remove this node if found
118     // return false if nothing is found or num is 0U
119     bool TakeUnits(uint32_t num, uint32_t& idx, bool refreshRegionDesc = true)
120     {
121         if (root_ == nullptr || num == 0) {
122             return false;
123         }
124 
125         return TakeUnitsImpl(num, idx, refreshRegionDesc);
126     }
127 
128     struct TreapNode {
TreapNodeTreapNode129         TreapNode(uint32_t idx, uint32_t num, bool refreshRegionDesc) : l(nullptr), r(nullptr), index_(idx), count_(num)
130         {
131             if (refreshRegionDesc) {
132                 RefreshFreeRegionDesc();
133             }
134         }
135 
~TreapNodeTreapNode136         ~TreapNode()
137         {
138             l = nullptr;
139             r = nullptr;
140         }
141 
GetIndexTreapNode142         inline uint32_t GetIndex() const { return index_; }
143 
GetCountTreapNode144         inline uint32_t GetCount() const { return count_; }
145 
IncCountTreapNode146         inline void IncCount(uint32_t num, bool refreshRegionDesc)
147         {
148             count_ += num;
149             if (refreshRegionDesc && count_ > 0) {
150                 RefreshFreeRegionDesc();
151             }
152         }
153 
ClearCountTreapNode154         inline void ClearCount() { count_ = 0; }
155 
UpdateNodeTreapNode156         inline void UpdateNode(uint32_t idx, uint32_t cnt, bool refreshRegionDesc)
157         {
158             index_ = idx;
159             count_ = cnt;
160             if (refreshRegionDesc && cnt > 0) {
161                 // GetNextNeighborRegion in compact gc expects free-region metadata is always up-to-date.
162                 // otherwise we can ignore refreshRegionDesc.
163                 RefreshFreeRegionDesc();
164             }
165         }
166 
IsProperNodeTreapNode167         inline bool IsProperNode() const
168         {
169             uint32_t leftCount = l == nullptr ? 0 : l->GetCount();
170             uint32_t rightCount = r == nullptr ? 0 : r->GetCount();
171             return (count_ >= leftCount && count_ >= rightCount);
172         }
173 
174         void RefreshFreeRegionDesc();
175         void ReleaseMemory();
176 
177         TreapNode* l;
178         TreapNode* r;
179 
180     private:
181         uint32_t index_;
182         uint32_t count_;
183     };
184 
185     // Iterator is defined specifically for releasing physical pages.
186     // It provides a "backwards" level-order traversal with right child node visited first
187     // rather than left child node first. This behaviour avoids that physical pages for regions
188     // are released and again committed for near future allocation.
189     class Iterator {
190     public:
Iterator(Treap & tree)191         explicit Iterator(Treap& tree) : localQueue_(tree.traversalSud_)
192         {
193             if (tree.root_ != nullptr) {
194                 localQueue_.Push(tree.root_);
195             }
196         }
197 
198         ~Iterator() = default;
199 
Next()200         inline TreapNode* Next()
201         {
202             if (localQueue_.Empty()) {
203                 return nullptr;
204             }
205             TreapNode* front = localQueue_.Front();
206             if (front->r != nullptr) {
207                 localQueue_.Push(front->r);
208             }
209             if (front->l != nullptr) {
210                 localQueue_.Push(front->l);
211             }
212             localQueue_.PopFront();
213             return front;
214         }
215 
216     private:
217         LocalDeque<TreapNode*> localQueue_;
218     };
219 
220     // very much like copy iterator in c++.
221     // traverse nodes by lvalue-reference.
222     class CopyIterator {
223     public:
CopyIterator(Treap & tree)224         explicit CopyIterator(Treap& tree) : localQueue_(tree.sud_)
225         {
226             if (tree.root_ != nullptr) {
227                 localQueue_.Push(&tree.root_);
228             }
229         }
230 
231         ~CopyIterator() = default;
232 
Next()233         inline TreapNode** Next()
234         {
235             if (localQueue_.Empty()) {
236                 return nullptr;
237             }
238             TreapNode** topElement = localQueue_.Front();
239             TreapNode* node = *topElement;
240             if (node->r != nullptr) {
241                 localQueue_.Push(&node->r);
242             }
243             if (node->l != nullptr) {
244                 localQueue_.Push(&node->l);
245             }
246             localQueue_.PopFront();
247             return topElement;
248         }
249 
250     private:
251         LocalDeque<TreapNode**> localQueue_;
252     };
253 
Empty()254     inline bool Empty() const { return root_ == nullptr; }
RootNode()255     inline const TreapNode* RootNode() const { return root_; }
256 
257     // root node records the largest block of memory.
ReleaseRootNode()258     void ReleaseRootNode()
259     {
260         root_->ReleaseMemory();
261         RemoveNode(root_);
262     }
263 
264     using RTAllocator = RTAllocatorT<sizeof(TreapNode), alignof(TreapNode)>;
265 
266 #ifndef NDEBUG
267     void DumpTree(const char* msg) const;
268 #endif
269 
270 private:
271     uint32_t totalCount_ = 0u; // sum of all node counts.
272     TreapNode* root_ = nullptr;
273     SingleUseDeque<TreapNode**> sud_;
274     SingleUseDeque<TreapNode*> traversalSud_;
275     RTAllocator nodeAllocator_;
276 
DeleteNode(TreapNode * n)277     void DeleteNode(TreapNode* n) noexcept
278     {
279         if (n == nullptr) {
280             return;
281         }
282         nodeAllocator_.Deallocate(n);
283     }
284 
285     // the following function tries to merge new node (idx, num) with n
286     enum class MergeResult {
287         MERGE_SUCCESS = 0, // successfully merged with the node n
288         MERGE_MISS,        // the new node (idx, num) is not connected to n, cannot merge
289         MERGE_ERROR        // error, operation aborted
290     };
291 
MergeAt(TreapNode & n,uint32_t idx,uint32_t num,bool refreshRegionDesc)292     MergeResult MergeAt(TreapNode& n, uint32_t idx, uint32_t num, bool refreshRegionDesc)
293     {
294         uint32_t endIdx = idx + num;
295 
296         // try to merge the inserted node to the right of n
297         if (idx == n.GetIndex() + n.GetCount()) {
298             return MergeToRight(n, endIdx, num, refreshRegionDesc);
299         }
300 
301         // try to merge the inserted node to the left of n
302         if (endIdx == n.GetIndex()) {
303             return MergeToLeft(n, idx, num, refreshRegionDesc);
304         }
305 
306         return MergeResult::MERGE_MISS;
307     }
308 
309     // merge free units to the right of the node. free units in the new merged node ends with endIdx,
310     // and we should also try to merge the nearest right node to the new node if possible.
MergeToRight(TreapNode & n,uint32_t endIdx,uint32_t num,bool refreshRegionDesc)311     MergeResult MergeToRight(TreapNode& n, uint32_t endIdx, uint32_t num, bool refreshRegionDesc)
312     {
313         // find the nearest right n of the new merged n which ends with endIdx.
314         TreapNode* parent = &n; // the parent of the *nearest* node.
315         TreapNode* nearest = n.r;
316         while (nearest != nullptr) {
317             if (nearest->GetIndex() == endIdx) {
318                 if (nearest->l != nullptr) {
319                     CTREE_ASSERT(false, "merging failed case 1");
320                     return MergeResult::MERGE_ERROR;
321                 }
322                 break;
323             } else if (nearest->GetIndex() < endIdx) {
324                 CTREE_ASSERT(false, "merging failed case 2");
325                 return MergeResult::MERGE_ERROR;
326             } else {
327                 parent = nearest;
328                 nearest = nearest->l;
329             }
330         }
331 
332         n.IncCount(num, refreshRegionDesc);
333         IncTotalCount(num);
334 
335         if (nearest != nullptr) {
336             n.IncCount(nearest->GetCount(), refreshRegionDesc);
337 
338             // now the node doesn't have left child, so we can fast remove it.
339             if (parent == &n) {
340                 n.r = nearest->r;
341             } else {
342                 parent->l = nearest->r;
343             }
344             nodeAllocator_.Deallocate(nearest);
345         }
346         CTREE_CHECK_PARENT_AND_RCHILD(&n);
347         return MergeResult::MERGE_SUCCESS;
348     }
349 
350     // merge free units to the left of the node n. free units in the new merged node starts with startIdx,
351     // and we should also try to merge the nearest left node to the new merged node if possible.
MergeToLeft(TreapNode & n,uint32_t startIdx,uint32_t num,bool refreshRegionDesc)352     MergeResult MergeToLeft(TreapNode& n, uint32_t startIdx, uint32_t num, bool refreshRegionDesc)
353     {
354         TreapNode* parent = &n; // the parent of the *nearest* node.
355         TreapNode* nearest = n.l;
356         while (nearest != nullptr) {
357             if (nearest->GetIndex() + nearest->GetCount() == startIdx) {
358                 if (nearest->r != nullptr) {
359                     CTREE_ASSERT(false, "merging failed case 3");
360                     return MergeResult::MERGE_ERROR;
361                 }
362                 break;
363             } else if (nearest->GetIndex() + nearest->GetCount() > startIdx) {
364                 CTREE_ASSERT(false, "merging failed case 4");
365                 return MergeResult::MERGE_ERROR;
366             } else {
367                 parent = nearest;
368                 nearest = nearest->r;
369             }
370         }
371 
372         n.UpdateNode(startIdx, n.GetCount() + num, refreshRegionDesc);
373         IncTotalCount(num);
374 
375         if (nearest != nullptr) {
376             // now the node doesn't have right child, so we can fast remove it.
377             if (parent == &n) {
378                 n.l = nearest->l;
379             } else {
380                 parent->r = nearest->l;
381             }
382             n.UpdateNode(nearest->GetIndex(), n.GetCount() + nearest->GetCount(), refreshRegionDesc);
383             nodeAllocator_.Deallocate(nearest);
384         }
385         CTREE_CHECK_PARENT_AND_LCHILD(&n);
386         return MergeResult::MERGE_SUCCESS;
387     }
388 
389     // see the public MergeInsert()
390     bool MergeInsertInternal(uint32_t idx, uint32_t num, bool refreshRegionDesc);
391 
392     // rotate the node and its left child to maintain heap property
RotateLeftChild(TreapNode & n)393     inline TreapNode* RotateLeftChild(TreapNode& n) const
394     {
395         TreapNode* newRoot = n.l;
396         n.l = newRoot->r;
397         newRoot->r = &n;
398         return newRoot;
399     }
400 
401     // rotate the node and its right child to maintain heap property
RotateRightChild(TreapNode & n)402     inline TreapNode* RotateRightChild(TreapNode& n) const
403     {
404         TreapNode* newRoot = n.r;
405         n.r = newRoot->l;
406         newRoot->l = &n;
407         return newRoot;
408     }
409 
TakeUnitsImpl(uint32_t num,uint32_t & idx,bool refershRegionDesc)410     bool TakeUnitsImpl(uint32_t num, uint32_t& idx, bool refershRegionDesc)
411     {
412         CopyIterator it(*this);
413         TreapNode** nodePtr = it.Next(); // pointer to root node
414         if (UNLIKELY_CC(nodePtr == nullptr)) {
415             return false;
416         }
417         TreapNode* node = *nodePtr;
418         if (node != nullptr && node->GetCount() < num) {
419             DLOG(REGION, "c-tree %p fail to take %u free units", this, num);
420             return false;
421         }
422         TreapNode** nextNodePtr = nullptr;
423         while ((nextNodePtr = it.Next()) != nullptr) {
424             TreapNode* nextNode = *nextNodePtr;
425             if (nextNode != nullptr && nextNode->GetCount() < num) {
426                 break;
427             }
428 
429             nodePtr = nextNodePtr;
430         }
431 
432         node = *nodePtr;
433         if (UNLIKELY_CC(node == nullptr)) {
434             return false;
435         }
436         idx = node->GetIndex();
437         auto count = node->GetCount();
438 
439         node->UpdateNode(idx + num, count - num, refershRegionDesc);
440         DecTotalCount(num);
441 
442         if (node->GetCount() == 0) {
443             RemoveZeroNode(*nodePtr);
444         } else {
445             LowerNonZeroNode(*nodePtr);
446         }
447 
448         CTREE_CHECK_PARENT_AND_LCHILD(*nodePtr);
449         CTREE_CHECK_PARENT_AND_RCHILD(*nodePtr);
450 
451         return true;
452     }
453 
AllocateLowestAddressFromNode(TreapNode * & node,uint32_t count,uint32_t & index)454     bool AllocateLowestAddressFromNode(TreapNode*& node, uint32_t count, uint32_t& index)
455     {
456         uint32_t nodeCount = node->GetCount();
457         if (nodeCount < count) {
458             return false;
459         }
460 
461         index = node->GetIndex();
462         DLOG(REGION, "c-tree %p v-alloc %u units from [%u+%u, %u)", this, count, index, nodeCount, index + nodeCount);
463 
464         node->UpdateNode(index + count, nodeCount - count, false);
465         DecTotalCount(count);
466         if (node->GetCount() == 0) {
467             RemoveZeroNode(node);
468         } else {
469             LowerNonZeroNode(node);
470         }
471         return true;
472     }
473 
474     // move node n down in the tree to maintain the heap property
LowerNode(TreapNode * n)475     NO_INLINE_CC TreapNode* LowerNode(TreapNode* n)
476     {
477         CTREE_ASSERT(n, "lowering node failed");
478         TreapNode* tmp = nullptr;
479 
480         if (n->l != nullptr && n->l->GetCount() > n->GetCount()) {
481             // this makes right tree slightly taller
482             if (n->r != nullptr && n->r->GetCount() > n->l->GetCount()) {
483                 tmp = RotateRightChild(*n);
484                 tmp->l = LowerNode(tmp->l);
485                 CTREE_CHECK_PARENT_AND_LCHILD(tmp);
486                 return tmp;
487             } else {
488                 tmp = RotateLeftChild(*n);
489                 tmp->r = LowerNode(tmp->r);
490                 CTREE_CHECK_PARENT_AND_RCHILD(tmp);
491                 return tmp;
492             }
493         }
494 
495         if (n->r != nullptr && n->r->GetCount() > n->GetCount()) {
496             tmp = RotateRightChild(*n);
497             tmp->l = LowerNode(tmp->l);
498             CTREE_CHECK_PARENT_AND_LCHILD(tmp);
499             return tmp;
500         }
501 
502         return n;
503     }
504 
505     // return the new position of node n.
LowerImproperNodeOnce(TreapNode * & n)506     TreapNode*& LowerImproperNodeOnce(TreapNode*& n) const
507     {
508         CTREE_ASSERT(n, "failed to lower node once");
509         if (n->l != nullptr) {
510             // use the child which has the max count to instead of node.
511             if (n->r != nullptr && n->r->GetCount() >= n->l->GetCount()) {
512                 TreapNode* newRoot = RotateRightChild(*n);
513                 CTREE_CHECK_PARENT_AND_LCHILD(newRoot);
514                 n = newRoot;
515                 return newRoot->l;
516             }
517             TreapNode* newRoot = RotateLeftChild(*n);
518             CTREE_CHECK_PARENT_AND_RCHILD(newRoot);
519             n = newRoot;
520             return newRoot->r;
521         }
522         // the node have right child only
523         if (n->r != nullptr) {
524             TreapNode* newRoot = RotateRightChild(*n);
525             CTREE_CHECK_PARENT_AND_LCHILD(newRoot);
526             n = newRoot;
527             return newRoot->l;
528         }
529         return n;
530     }
531 
532     // maitain the heap property for subtree with root node n. assume n->GetCount() returns 0 for now.
533     // return the new position of node n.
MaintainHeapPropertyForZeroNode(TreapNode * & n)534     TreapNode*& MaintainHeapPropertyForZeroNode(TreapNode*& n) const
535     {
536         TreapNode** nodePtr = &n;
537         while ((*nodePtr)->l != nullptr || (*nodePtr)->r != nullptr) {
538             nodePtr = &LowerImproperNodeOnce(*nodePtr);
539         }
540         return *nodePtr;
541     }
542 
543     // maitain the heap property for subtree with root node n. assume n->GetCount() is less than its children.
544     // return the new position of node n.
MaintainHeapPropertyForNonZeroNode(TreapNode * & n)545     void MaintainHeapPropertyForNonZeroNode(TreapNode*& n) const
546     {
547         TreapNode** nodePtr = &n;
548         // *nodePtr won't be nullptr, don't need to check it.
549         while (!(**nodePtr).IsProperNode()) {
550             nodePtr = &LowerImproperNodeOnce(*nodePtr);
551         }
552     }
553 
RemoveZeroNode(TreapNode * & n)554     void RemoveZeroNode(TreapNode*& n)
555     {
556         TreapNode*& nodeRef = MaintainHeapPropertyForZeroNode(n);
557         LOGF_CHECK((nodeRef->l == nullptr && nodeRef->r == nullptr)) << "UNEXPECT";
558         nodeAllocator_.Deallocate(nodeRef);
559         nodeRef = nullptr;
560     }
561 
RemoveNode(TreapNode * & n)562     void RemoveNode(TreapNode*& n)
563     {
564         DecTotalCount(n->GetCount());
565         n->ClearCount();
566         RemoveZeroNode(n);
567     }
568 
569     // move node n down in the tree to maintain the heap property
LowerNonZeroNode(TreapNode * & n)570     void LowerNonZeroNode(TreapNode*& n) const { MaintainHeapPropertyForNonZeroNode(n); }
571 
572 #ifndef NDEBUG
CheckParentAndLeftChild(const TreapNode * n)573     inline void CheckParentAndLeftChild(const TreapNode* n) const
574     {
575         if (n != nullptr) {
576             const TreapNode* l = n->l;
577             if (l != nullptr) {
578                 CTREE_ASSERT((n->GetIndex() > (l->GetIndex() + l->GetCount())), "left child overlapped with parent");
579                 CTREE_ASSERT((n->GetCount() >= l->GetCount()), "left child bigger than parent");
580             }
581         }
582     }
CheckParentAndRightChild(const TreapNode * n)583     inline void CheckParentAndRightChild(const TreapNode* n) const
584     {
585         if (n != nullptr) {
586             const TreapNode* r = n->r;
587             if (r != nullptr) {
588                 CTREE_ASSERT(((n->GetIndex() + n->GetCount()) < r->GetIndex()), "right child overlapped with parent");
589                 CTREE_ASSERT((n->GetCount() >= r->GetCount()), "right child bigger than parent");
590             }
591         }
592     }
593 
CheckNode(const TreapNode * n)594     inline void CheckNode(const TreapNode* n) const
595     {
596         CheckParentAndLeftChild(n);
597         CheckParentAndRightChild(n);
598     }
599 
VerifyTree()600     void VerifyTree()
601     {
602         uint32_t total = 0;
603         Treap::Iterator it(*this);
604         TreapNode* node = it.Next();
605         while (node != nullptr) {
606             total += node->GetCount();
607             CheckNode(node);
608             node = it.Next();
609         }
610 
611         if (total != GetTotalCount()) { //LCOV_EXCL_BR_LINE
612             DLOG(REGION, "c-tree %p total unit count %u (expect %u)", this, GetTotalCount(), total);
613             DumpTree("internal error tree");
614             LOG_COMMON(FATAL) << "Treap::VerifyTree() Should not execute here, abort.";
615             UNREACHABLE_CC();
616         }
617     }
618 #endif
619 };
620 } // namespace common
621 
622 #endif  // COMMON_COMPONENTS_HEAP_ALLOCATOR_TREAP_H
623