LCOV - code coverage report
Current view: top level - boost/capy/ex - recycling_memory_resource.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 81.4 % 59 48
Test Date: 2026-01-30 23:43:15 Functions: 90.0 % 10 9

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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              : #ifndef BOOST_CAPY_RECYCLING_MEMORY_RESOURCE_HPP
      11              : #define BOOST_CAPY_RECYCLING_MEMORY_RESOURCE_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : 
      15              : #include <cstddef>
      16              : #include <memory_resource>
      17              : #include <mutex>
      18              : 
      19              : namespace boost {
      20              : namespace capy {
      21              : 
      22              : /** Recycling memory resource with thread-local and global pools.
      23              : 
      24              :     This memory resource recycles memory blocks to reduce allocation
      25              :     overhead for coroutine frames. It maintains a thread-local pool
      26              :     for fast lock-free access and a global pool for cross-thread
      27              :     block sharing.
      28              : 
      29              :     Blocks are tracked by size to avoid returning undersized blocks.
      30              : 
      31              :     This is the default allocator used by run_async when no allocator
      32              :     is specified.
      33              : 
      34              :     @par Thread Safety
      35              :     Thread-safe. The thread-local pool requires no synchronization.
      36              :     The global pool uses a mutex for cross-thread access.
      37              : 
      38              :     @see get_recycling_memory_resource
      39              :     @see run_async
      40              : */
      41              : class recycling_memory_resource : public std::pmr::memory_resource
      42              : {
      43              :     struct block
      44              :     {
      45              :         block* next;
      46              :         std::size_t size;
      47              :     };
      48              : 
      49              :     struct global_pool
      50              :     {
      51              :         std::mutex mtx;
      52              :         block* head = nullptr;
      53              : 
      54           23 :         ~global_pool()
      55              :         {
      56           23 :             while(head)
      57              :             {
      58            0 :                 auto p = head;
      59            0 :                 head = head->next;
      60            0 :                 ::operator delete(p);
      61              :             }
      62           23 :         }
      63              : 
      64              :         void push(block* b)
      65              :         {
      66              :             std::lock_guard<std::mutex> lock(mtx);
      67              :             b->next = head;
      68              :             head = b;
      69              :         }
      70              : 
      71           91 :         block* pop(std::size_t n)
      72              :         {
      73           91 :             std::lock_guard<std::mutex> lock(mtx);
      74           91 :             block** pp = &head;
      75           91 :             while(*pp)
      76              :             {
      77            0 :                 if((*pp)->size >= n + sizeof(block))
      78              :                 {
      79            0 :                     block* p = *pp;
      80            0 :                     *pp = p->next;
      81            0 :                     return p;
      82              :                 }
      83            0 :                 pp = &(*pp)->next;
      84              :             }
      85           91 :             return nullptr;
      86           91 :         }
      87              :     };
      88              : 
      89              :     struct local_pool
      90              :     {
      91              :         block* head = nullptr;
      92              : 
      93           24 :         ~local_pool()
      94              :         {
      95          115 :             while(head)
      96              :             {
      97           91 :                 auto p = head;
      98           91 :                 head = head->next;
      99           91 :                 ::operator delete(p);
     100              :             }
     101           24 :         }
     102              : 
     103         4362 :         void push(block* b)
     104              :         {
     105         4362 :             b->next = head;
     106         4362 :             head = b;
     107         4362 :         }
     108              : 
     109         4362 :         block* pop(std::size_t n)
     110              :         {
     111         4362 :             block** pp = &head;
     112         5922 :             while(*pp)
     113              :             {
     114         5831 :                 if((*pp)->size >= n + sizeof(block))
     115              :                 {
     116         4271 :                     block* p = *pp;
     117         4271 :                     *pp = p->next;
     118         4271 :                     return p;
     119              :                 }
     120         1560 :                 pp = &(*pp)->next;
     121              :             }
     122           91 :             return nullptr;
     123              :         }
     124              :     };
     125              : 
     126         8724 :     static local_pool& local()
     127              :     {
     128         8724 :         static thread_local local_pool pool;
     129         8724 :         return pool;
     130              :     }
     131              : 
     132           91 :     static global_pool& global()
     133              :     {
     134           91 :         static global_pool pool;
     135           91 :         return pool;
     136              :     }
     137              : 
     138              : protected:
     139              :     void*
     140         4362 :     do_allocate(std::size_t bytes, std::size_t) override
     141              :     {
     142         4362 :         std::size_t total = bytes + sizeof(block);
     143              : 
     144         4362 :         if(auto* b = local().pop(bytes))
     145         4271 :             return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
     146              : 
     147           91 :         if(auto* b = global().pop(bytes))
     148            0 :             return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
     149              : 
     150           91 :         auto* b = static_cast<block*>(::operator new(total));
     151           91 :         b->next = nullptr;
     152           91 :         b->size = total;
     153           91 :         return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
     154              :     }
     155              : 
     156              :     void
     157         4362 :     do_deallocate(void* p, std::size_t, std::size_t) override
     158              :     {
     159         4362 :         auto* b = static_cast<block*>(
     160              :             static_cast<void*>(static_cast<char*>(p) - sizeof(block)));
     161         4362 :         b->next = nullptr;
     162         4362 :         local().push(b);
     163         4362 :     }
     164              : 
     165              :     bool
     166            0 :     do_is_equal(const memory_resource& other) const noexcept override
     167              :     {
     168            0 :         return this == &other;
     169              :     }
     170              : };
     171              : 
     172              : /** Returns pointer to the default recycling memory resource.
     173              : 
     174              :     The returned pointer is valid for the lifetime of the program.
     175              :     This is the default allocator used by run_async.
     176              : 
     177              :     @return Pointer to the recycling memory resource.
     178              : 
     179              :     @see recycling_memory_resource
     180              :     @see run_async
     181              : */
     182              : BOOST_CAPY_DECL
     183              : std::pmr::memory_resource*
     184              : get_recycling_memory_resource() noexcept;
     185              : 
     186              : } // namespace capy
     187              : } // namespace boost
     188              : 
     189              : #endif
        

Generated by: LCOV version 2.3