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