Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
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 : : #include <hpactor/hpactor_config.hpp>
16 : : #include <hpactor/log/log_field.hpp>
17 : : #include <hpactor/log/logger.hpp>
18 : : #include <hpactor/mem/slab_cache.hpp>
19 : :
20 : : #include <cstring>
21 : :
22 : : namespace hpactor::mem {
23 : :
24 : : #if HPACTOR_ENABLE_MEMORY_DEBUG
25 : : namespace {
26 : : inline constexpr uint8_t kPoisonByte = 0xAA;
27 : : } // namespace
28 : : #endif
29 : :
30 : 229 : SlabCache::~SlabCache() {
31 : 3781 : for (auto* slab : slabs_) {
32 : 3552 : SegmentProvider::instance().release_slab(slab, size_class_);
33 : : }
34 : 229 : }
35 : :
36 : 1258833 : void* SlabCache::allocate(ActorId owner) noexcept {
37 : : // 1. Try freelist first
38 : 1258833 : auto* block = freelist_.pop();
39 : 1258833 : if (block) {
40 : : #if HPACTOR_ENABLE_MEMORY_DEBUG
41 : : // Verify canary was not corrupted while block was freed
42 : : size_t bs = block_size(size_class_);
43 : : if (!CanaryFooter::verify(block, bs)) {
44 : : HPACTOR_LOG_ERROR(
45 : : log::LogCategory::kMemory, ActorId{0},
46 : : static_cast<uint32_t>(log::LogEventId::kMemoryCorruption),
47 : : "memory corruption detected (canary mismatch in allocate)",
48 : : log::field_ptr("block", block));
49 : : stats_.alloc_count.fetch_add(1, std::memory_order_relaxed);
50 : : live_count_.fetch_add(1, std::memory_order_relaxed);
51 : : return nullptr; // corruption detected, refuse allocation
52 : : }
53 : : // Check poison pattern — first bytes should be 0xAA
54 : : auto* user = static_cast<uint8_t*>(block->user_data());
55 : : size_t usz = user_size(bs);
56 : : for (size_t i = 0; i < usz && i < 16; ++i) {
57 : : if (user[i] != kPoisonByte) {
58 : : // use-after-free detected
59 : : HPACTOR_LOG_ERROR(
60 : : log::LogCategory::kMemory, ActorId{0},
61 : : static_cast<uint32_t>(log::LogEventId::kMemoryCorruption),
62 : : "use-after-free detected (poison byte violated)",
63 : : log::field_ptr("block", block));
64 : : stats_.alloc_count.fetch_add(1, std::memory_order_relaxed);
65 : : live_count_.fetch_add(1, std::memory_order_relaxed);
66 : : return nullptr;
67 : : }
68 : : }
69 : : #endif
70 : 100114 : block->owner_id = owner.value();
71 : 100114 : block->magic = kAllocMagic;
72 : 100114 : block->generation = current_generation_;
73 : 100114 : stats_.alloc_count.fetch_add(1, std::memory_order_relaxed);
74 : 100114 : live_count_.fetch_add(1, std::memory_order_relaxed);
75 : 100114 : return block->user_data();
76 : : }
77 : :
78 : : // 2. Bump allocate from current slab
79 : 1158719 : if (current_slab_) {
80 : 1158635 : size_t bs = block_size(size_class_);
81 : 1158635 : if (bump_offset_ + bs <= slab_size_) {
82 : 1155167 : auto* raw = current_slab_ + bump_offset_;
83 : 1155167 : bump_offset_ += bs;
84 : 1155167 : auto* hdr = AllocHeader::stamp(raw, size_class_, owner);
85 : 1155167 : hdr->generation = current_generation_;
86 : 1155167 : CanaryFooter::stamp(hdr, bs);
87 : 1155167 : stats_.alloc_count.fetch_add(1, std::memory_order_relaxed);
88 : 1155167 : live_count_.fetch_add(1, std::memory_order_relaxed);
89 : 1155167 : return hdr->user_data();
90 : : }
91 : : }
92 : :
93 : : // 3. Need a new slab
94 : 3552 : refill();
95 : 3552 : if (current_slab_) {
96 : 3552 : return allocate(owner);
97 : : }
98 : 0 : return nullptr;
99 : : }
100 : :
101 : 1155131 : void SlabCache::deallocate(void* user_ptr) noexcept {
102 : 1155131 : auto* hdr = AllocHeader::from_user_data(user_ptr);
103 : :
104 : : #if HPACTOR_ENABLE_MEMORY_DEBUG
105 : : size_t bs = block_size(size_class_);
106 : : // Verify canary before freeing (catch buffer overflow)
107 : : if (!CanaryFooter::verify(hdr, bs)) {
108 : : HPACTOR_LOG_ERROR(log::LogCategory::kMemory, ActorId{0},
109 : : static_cast<uint32_t>(log::LogEventId::kMemoryCorruption),
110 : : "memory corruption detected (canary mismatch in "
111 : : "deallocate)",
112 : : log::field_ptr("ptr", user_ptr));
113 : : }
114 : : // Poison user data to catch use-after-free
115 : : std::memset(user_ptr, kPoisonByte, user_size(bs));
116 : : #endif
117 : :
118 : 1155131 : hdr->magic = kFreedMagic;
119 : 1155131 : stats_.free_count.fetch_add(1, std::memory_order_relaxed);
120 : 1155131 : live_count_.fetch_sub(1, std::memory_order_relaxed);
121 : 1155131 : freelist_.push(hdr);
122 : 1155131 : }
123 : :
124 : 3552 : void SlabCache::refill() {
125 : 3552 : current_slab_ = static_cast<std::byte*>(
126 : 3552 : SegmentProvider::instance().acquire_slab(size_class_));
127 : 3552 : if (current_slab_) {
128 : 3552 : slab_size_ = SegmentProvider::instance().slab_size(size_class_);
129 : 3552 : bump_offset_ = 0;
130 : 3552 : slabs_.push_back(current_slab_);
131 : 3552 : stats_.slab_acquire_count.fetch_add(1, std::memory_order_relaxed);
132 : : }
133 : 3552 : }
134 : :
135 : : } // namespace hpactor::mem
|