LCOV - code coverage report
Current view: top level - src/mem - segment_provider.cpp (source / functions) Coverage Total Hit
Test: HPActor Coverage Lines: 88.2 % 76 67
Test Date: 2026-05-20 02:24:49 Functions: 100.0 % 8 8
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             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
        

Generated by: LCOV version 2.0-1