src/ex/recycling_memory_resource.cpp
100.0% Lines (43/43)
100.0% List of functions (11/11)
Functions (11)
Function
Calls
Lines
Blocks
boost::capy::recycling_memory_resource::~recycling_memory_resource()
:21
306x
100.0%
100.0%
boost::capy::recycling_memory_resource::arm_thread_cleanup()
:24
7088x
100.0%
100.0%
boost::capy::recycling_memory_resource::arm_thread_cleanup()::janitor::~janitor()
:28
297x
100.0%
100.0%
boost::capy::recycling_memory_resource::global()
:44
13075x
100.0%
100.0%
boost::capy::recycling_memory_resource::global()::holder::~holder()
:55
35x
100.0%
100.0%
boost::capy::recycling_memory_resource::global_mutex()
:68
13110x
100.0%
100.0%
boost::capy::recycling_memory_resource::allocate_slow(unsigned long, unsigned long)
:81
6797x
100.0%
100.0%
boost::capy::recycling_memory_resource::deallocate_slow(void*, unsigned long)
:94
6278x
100.0%
100.0%
boost::capy::recycling_memory_resource::do_allocate(unsigned long, unsigned long)
:106
2639x
100.0%
100.0%
boost::capy::recycling_memory_resource::do_deallocate(void*, unsigned long, unsigned long)
:112
2639x
100.0%
100.0%
boost::capy::get_recycling_memory_resource()
:118
2908x
100.0%
100.0%
| Line | TLA | Hits | 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 | 306x | recycling_memory_resource::~recycling_memory_resource() = default; | |
| 22 | |||
| 23 | void | ||
| 24 | 7088x | recycling_memory_resource::arm_thread_cleanup() noexcept | |
| 25 | { | ||
| 26 | struct janitor | ||
| 27 | { | ||
| 28 | 297x | ~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 | 297x | auto& lp = local(); | |
| 34 | 2079x | for(auto& b : lp.buckets) | |
| 35 | 6036x | while(b.count > 0) | |
| 36 | 4254x | ::operator delete(b.pop()); | |
| 37 | 297x | } | |
| 38 | }; | ||
| 39 | 7088x | static thread_local janitor j; | |
| 40 | (void)j; | ||
| 41 | 7088x | } | |
| 42 | |||
| 43 | recycling_memory_resource::pool& | ||
| 44 | 13075x | 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 | 35x | ~holder() | |
| 56 | { | ||
| 57 | 35x | std::lock_guard<std::mutex> lock(global_mutex()); | |
| 58 | 245x | for(auto& b : p.buckets) | |
| 59 | 251x | while(b.count > 0) | |
| 60 | 41x | ::operator delete(b.pop()); | |
| 61 | 35x | } | |
| 62 | }; | ||
| 63 | 13075x | static holder h; | |
| 64 | 13075x | return h.p; | |
| 65 | } | ||
| 66 | |||
| 67 | std::mutex& | ||
| 68 | 13110x | 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 | 13110x | ::new(static_cast<void*>(storage)) std::mutex(); | |
| 77 | 13110x | return *mtx; | |
| 78 | } | ||
| 79 | |||
| 80 | void* | ||
| 81 | 6797x | recycling_memory_resource::allocate_slow( | |
| 82 | std::size_t rounded, std::size_t idx) | ||
| 83 | { | ||
| 84 | 6797x | arm_thread_cleanup(); | |
| 85 | { | ||
| 86 | 6797x | std::lock_guard<std::mutex> lock(global_mutex()); | |
| 87 | 6797x | if(auto* p = global().buckets[idx].pop(local().buckets[idx])) | |
| 88 | 547x | return p; | |
| 89 | 6797x | } | |
| 90 | 6250x | return ::operator new(rounded); | |
| 91 | } | ||
| 92 | |||
| 93 | void | ||
| 94 | 6278x | recycling_memory_resource::deallocate_slow( | |
| 95 | void* p, std::size_t idx) | ||
| 96 | { | ||
| 97 | { | ||
| 98 | 6278x | std::lock_guard<std::mutex> lock(global_mutex()); | |
| 99 | 6278x | if(global().buckets[idx].push(p)) | |
| 100 | 4323x | return; | |
| 101 | 6278x | } | |
| 102 | 1955x | ::operator delete(p); | |
| 103 | } | ||
| 104 | |||
| 105 | void* | ||
| 106 | 2639x | recycling_memory_resource::do_allocate(std::size_t bytes, std::size_t alignment) | |
| 107 | { | ||
| 108 | 2639x | return allocate_fast(bytes, alignment); | |
| 109 | } | ||
| 110 | |||
| 111 | void | ||
| 112 | 2639x | recycling_memory_resource::do_deallocate(void* p, std::size_t bytes, std::size_t alignment) | |
| 113 | { | ||
| 114 | 2639x | deallocate_fast(p, bytes, alignment); | |
| 115 | 2639x | } | |
| 116 | |||
| 117 | std::pmr::memory_resource* | ||
| 118 | 2908x | get_recycling_memory_resource() noexcept | |
| 119 | { | ||
| 120 | 2908x | static recycling_memory_resource instance; | |
| 121 | 2908x | return &instance; | |
| 122 | } | ||
| 123 | |||
| 124 | } // namespace capy | ||
| 125 | } // namespace boost | ||
| 126 |