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/log/log_field.hpp>
16 : : #include <hpactor/log/logger.hpp>
17 : : #include <hpactor/mem/segment_provider.hpp>
18 : : #include <hpactor/platform.hpp>
19 : :
20 : : #include <algorithm>
21 : : #include <cstring>
22 : : #include <sys/mman.h>
23 : :
24 : : namespace hpactor::mem {
25 : :
26 : 10657 : SegmentProvider& SegmentProvider::instance() {
27 : 10657 : static SegmentProvider sp;
28 : 10657 : return sp;
29 : : }
30 : :
31 : 3556 : void* SegmentProvider::acquire_slab(SizeClass sc) {
32 : 3556 : std::lock_guard<std::mutex> lock(mutex_);
33 : :
34 : 3556 : void* slab = carve_from_segment(sc);
35 : 3556 : if (slab) {
36 : 3024 : return slab;
37 : : }
38 : :
39 : 532 : return allocate_new_segment(slab_size(sc));
40 : 3556 : }
41 : :
42 : 3556 : void SegmentProvider::release_slab(void* slab, SizeClass /*sc*/) {
43 : 3556 : std::lock_guard<std::mutex> lock(mutex_);
44 : :
45 : 3556 : auto it = addr_to_segment_.find(slab);
46 : 3556 : if (it == addr_to_segment_.end()) {
47 : 0 : return;
48 : : }
49 : :
50 : 3556 : uint32_t idx = it->second;
51 : 3556 : addr_to_segment_.erase(it);
52 : :
53 : 3556 : if (idx >= segments_.size()) {
54 : 0 : return;
55 : : }
56 : :
57 : 3556 : if (segments_[idx].dec_ref() == 0) {
58 : : // Last slab — munmap the whole segment
59 : 532 : munmap(segments_[idx].base, segments_[idx].size);
60 : 532 : segments_.erase(segments_.begin() + idx);
61 : : // Update indices for slabs in segments that shifted
62 : 262765 : for (auto& [ptr, i] : addr_to_segment_) {
63 : 262233 : if (i > idx) {
64 : 148417 : --i;
65 : : }
66 : : }
67 : : }
68 : 3556 : }
69 : :
70 : 1 : SegmentProvider::SegmentInfo SegmentProvider::lookup(void* ptr) const {
71 : 1 : std::lock_guard<std::mutex> lock(mutex_);
72 : :
73 : 1 : auto it = addr_to_segment_.find(ptr);
74 : 1 : if (it != addr_to_segment_.end()) {
75 : 1 : auto idx = it->second;
76 : 1 : if (idx < segments_.size()) {
77 : 1 : return {segments_[idx].base, segments_[idx].size};
78 : : }
79 : 0 : return {nullptr, 0};
80 : : }
81 : :
82 : : // Linear scan for interior pointers
83 : 0 : for (const auto& seg : segments_) {
84 : 0 : if (ptr >= seg.base && ptr < static_cast<std::byte*>(seg.base) + seg.size) {
85 : 0 : return {seg.base, seg.size};
86 : : }
87 : : }
88 : 0 : return {nullptr, 0};
89 : 1 : }
90 : :
91 : 7645 : size_t SegmentProvider::slab_size(SizeClass sc) const {
92 : : // Multiplier per size class (base = 64KB)
93 : : static constexpr uint8_t kMultiplier[kNumSizeClasses] = {
94 : : 1, 1, 1, // 32B, 64B, 128B → 64KB
95 : : 2, // 256B → 128KB
96 : : 4, 4, // 512B, 1KB → 256KB
97 : : 8, 8 // 2KB, 4KB → 512KB
98 : : };
99 : 7645 : return kBaseSlabSize * kMultiplier[static_cast<uint8_t>(sc)];
100 : : }
101 : :
102 : 1 : SegmentProvider::Stats SegmentProvider::stats() const {
103 : 1 : std::lock_guard<std::mutex> lock(mutex_);
104 : 1 : Stats s;
105 : 1 : s.active_segments = segments_.size();
106 : 2 : for (const auto& seg : segments_) {
107 : 1 : s.total_allocated += seg.size;
108 : : }
109 : 2 : return s;
110 : 1 : }
111 : :
112 : 3556 : void* SegmentProvider::carve_from_segment(SizeClass sc) {
113 : 3556 : size_t needed = slab_size(sc);
114 : 828623 : for (size_t i = 0; i < segments_.size(); ++i) {
115 : 828091 : auto& seg = segments_[i];
116 : 828091 : if (seg.size - seg.offset >= needed) {
117 : 3024 : void* slab = static_cast<std::byte*>(seg.base) + seg.offset;
118 : 3024 : seg.offset += needed;
119 : 3024 : seg.inc_ref();
120 : 3024 : addr_to_segment_[slab] = static_cast<uint32_t>(i);
121 : 3024 : return slab;
122 : : }
123 : : }
124 : 532 : return nullptr;
125 : : }
126 : :
127 : 532 : void* SegmentProvider::allocate_new_segment(size_t size) {
128 : 532 : size_t alloc_size = (size > kSegmentSize) ? size : kSegmentSize;
129 : :
130 : 532 : void* base = mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE,
131 : 532 : MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
132 : :
133 : 532 : if (base == MAP_FAILED) {
134 : 0 : HPACTOR_LOG_WARNING(log::LogCategory::kMemory, ActorId{0},
135 : : static_cast<uint32_t>(log::LogEventId::kMemoryAlloc),
136 : : "segment allocation failed (mmap)",
137 : : log::field("size", static_cast<uint64_t>(alloc_size)));
138 : 0 : return nullptr;
139 : : }
140 : :
141 : 532 : Segment seg;
142 : 532 : seg.base = base;
143 : 532 : seg.size = alloc_size;
144 : 532 : seg.offset = size; // first `size` bytes are the returned slab
145 : 532 : seg.ref_count = 1;
146 : :
147 : 532 : segments_.push_back(seg);
148 : 532 : uint32_t idx = static_cast<uint32_t>(segments_.size() - 1);
149 : 532 : addr_to_segment_[base] = idx;
150 : :
151 : 532 : return base;
152 : : }
153 : :
154 : : } // namespace hpactor::mem
|