LCOV - code coverage report
Current view: top level - /jenkins/workspace/boost-root/libs/capy/src/ex - recycling_memory_resource.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 100.0 % 43 43
Test Date: 2026-07-23 14:07:55 Functions: 91.7 % 12 11 1

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/capy
       8                 : //
       9                 : 
      10                 : #include <boost/capy/ex/recycling_memory_resource.hpp>
      11                 : 
      12                 : #include <new>
      13                 : 
      14                 : namespace boost {
      15                 : namespace capy {
      16                 : 
      17                 : // Instance destruction does nothing: the resource is stateless (all pools
      18                 : // are static). Global-pool cleanup is owned by global()'s holder, exactly
      19                 : // as in the original where the global pool's own destructor drained it,
      20                 : // independent of any instance lifetime.
      21 HIT         306 : recycling_memory_resource::~recycling_memory_resource() = default;
      22                 : 
      23                 : void
      24            7321 : recycling_memory_resource::arm_thread_cleanup() noexcept
      25                 : {
      26                 :     struct janitor
      27                 :     {
      28             297 :         ~janitor()
      29                 :         {
      30                 :             // Return this thread's cached blocks to the OS so nothing
      31                 :             // leaks at thread exit. The pool has a trivial dtor, so its
      32                 :             // thread-local storage is still valid here.
      33             297 :             auto& lp = local();
      34            2079 :             for(auto& b : lp.buckets)
      35            6036 :                 while(b.count > 0)
      36            4254 :                     ::operator delete(b.pop());
      37             297 :         }
      38                 :     };
      39            7321 :     static thread_local janitor j;
      40                 :     (void)j;
      41            7321 : }
      42                 : 
      43                 : recycling_memory_resource::pool&
      44           13380 : recycling_memory_resource::global() noexcept
      45                 : {
      46                 :     // Holder gives the global pool a destructor (the trivial-dtor pool type
      47                 :     // itself must stay guard-free for local()). Runs unconditionally at
      48                 :     // process exit, mirroring the original global pool destructor, and is
      49                 :     // locked because a worker thread may still be in a slow path. This path
      50                 :     // is cold (slow paths only), so the holder's guard costs nothing hot.
      51                 :     struct holder
      52                 :     {
      53                 :         pool p;
      54                 : 
      55              35 :         ~holder()
      56                 :         {
      57              35 :             std::lock_guard<std::mutex> lock(global_mutex());
      58             245 :             for(auto& b : p.buckets)
      59             251 :                 while(b.count > 0)
      60              41 :                     ::operator delete(b.pop());
      61              35 :         }
      62                 :     };
      63           13380 :     static holder h;
      64           13380 :     return h.p;
      65                 : }
      66                 : 
      67                 : std::mutex&
      68           13415 : recycling_memory_resource::global_mutex() noexcept
      69                 : {
      70                 :     // Never destroyed: it is locked during process-exit teardown (in
      71                 :     // global()'s holder destructor), after a function-local `static
      72                 :     // std::mutex` could itself have been destroyed. Placement-new into
      73                 :     // static storage owns no heap allocation, so there is nothing to leak.
      74                 :     alignas(std::mutex) static unsigned char storage[sizeof(std::mutex)];
      75                 :     static std::mutex* const mtx =
      76           13415 :         ::new(static_cast<void*>(storage)) std::mutex();
      77           13415 :     return *mtx;
      78                 : }
      79                 : 
      80                 : void*
      81            7030 : recycling_memory_resource::allocate_slow(
      82                 :     std::size_t rounded, std::size_t idx)
      83                 : {
      84            7030 :     arm_thread_cleanup();
      85                 :     {
      86            7030 :         std::lock_guard<std::mutex> lock(global_mutex());
      87            7030 :         if(auto* p = global().buckets[idx].pop(local().buckets[idx]))
      88             514 :             return p;
      89            7030 :     }
      90            6516 :     return ::operator new(rounded);
      91                 : }
      92                 : 
      93                 : void
      94            6350 : recycling_memory_resource::deallocate_slow(
      95                 :     void* p, std::size_t idx)
      96                 : {
      97                 :     {
      98            6350 :         std::lock_guard<std::mutex> lock(global_mutex());
      99            6350 :         if(global().buckets[idx].push(p))
     100            4129 :             return;
     101            6350 :     }
     102            2221 :     ::operator delete(p);
     103                 : }
     104                 : 
     105                 : void*
     106            2645 : recycling_memory_resource::do_allocate(std::size_t bytes, std::size_t alignment)
     107                 : {
     108            2645 :     return allocate_fast(bytes, alignment);
     109                 : }
     110                 : 
     111                 : void
     112            2645 : recycling_memory_resource::do_deallocate(void* p, std::size_t bytes, std::size_t alignment)
     113                 : {
     114            2645 :     deallocate_fast(p, bytes, alignment);
     115            2645 : }
     116                 : 
     117                 : std::pmr::memory_resource*
     118            2900 : get_recycling_memory_resource() noexcept
     119                 : {
     120            2900 :     static recycling_memory_resource instance;
     121            2900 :     return &instance;
     122                 : }
     123                 : 
     124                 : } // namespace capy
     125                 : } // namespace boost
        

Generated by: LCOV version 2.3