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