GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/ex/detail/recycling_frame_allocator.hpp
Date: 2026-01-15 20:40:20
Exec Total Coverage
Lines: 0 57 0.0%
Functions: 0 9 0.0%
Branches: 0 23 0.0%

Line Branch Exec Source
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/corosio
8 //
9
10 #ifndef BOOST_CAPY_DETAIL_RECYCLING_FRAME_ALLOCATOR_HPP
11 #define BOOST_CAPY_DETAIL_RECYCLING_FRAME_ALLOCATOR_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/ex/frame_allocator.hpp>
15
16 #include <cstddef>
17 #include <mutex>
18
19 namespace boost {
20 namespace capy {
21 namespace detail {
22
23 /** Recycling frame allocator with thread-local and global pools.
24
25 This allocator recycles memory blocks to reduce allocation overhead.
26 It maintains a thread-local pool for fast lock-free access and a
27 global pool for cross-thread block sharing.
28
29 Blocks are tracked by size to avoid returning undersized blocks.
30
31 This type satisfies the frame_allocator concept and is cheaply
32 copyable (all instances share the same static pools).
33 */
34 class recycling_frame_allocator
35 {
36 struct block
37 {
38 block* next;
39 std::size_t size;
40 };
41
42 struct global_pool
43 {
44 std::mutex mtx;
45 block* head = nullptr;
46
47 ~global_pool()
48 {
49 while(head)
50 {
51 auto p = head;
52 head = head->next;
53 ::operator delete(p);
54 }
55 }
56
57 void push(block* b)
58 {
59 std::lock_guard<std::mutex> lock(mtx);
60 b->next = head;
61 head = b;
62 }
63
64 block* pop(std::size_t n)
65 {
66 std::lock_guard<std::mutex> lock(mtx);
67 block** pp = &head;
68 while(*pp)
69 {
70 // block->size stores total allocated size (including header)
71 if((*pp)->size >= n + sizeof(block))
72 {
73 block* p = *pp;
74 *pp = p->next;
75 return p;
76 }
77 pp = &(*pp)->next;
78 }
79 return nullptr;
80 }
81 };
82
83 struct local_pool
84 {
85 block* head = nullptr;
86
87 ~local_pool()
88 {
89 while(head)
90 {
91 auto p = head;
92 head = head->next;
93 ::operator delete(p);
94 }
95 }
96
97 void push(block* b)
98 {
99 b->next = head;
100 head = b;
101 }
102
103 block* pop(std::size_t n)
104 {
105 block** pp = &head;
106 while(*pp)
107 {
108 // block->size stores total allocated size (including header)
109 if((*pp)->size >= n + sizeof(block))
110 {
111 block* p = *pp;
112 *pp = p->next;
113 return p;
114 }
115 pp = &(*pp)->next;
116 }
117 return nullptr;
118 }
119 };
120
121 static local_pool& local()
122 {
123 static thread_local local_pool local;
124 return local;
125 }
126
127 static global_pool& global()
128 {
129 static global_pool pool;
130 return pool;
131 }
132
133 public:
134 void* allocate(std::size_t n)
135 {
136 std::size_t total = n + sizeof(block);
137
138 if(auto* b = local().pop(n))
139 return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
140
141 if(auto* b = global().pop(n))
142 return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
143
144 auto* b = static_cast<block*>(::operator new(total));
145 b->next = nullptr;
146 b->size = total;
147 return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
148 }
149
150 void deallocate(void* p, std::size_t)
151 {
152 auto* b = static_cast<block*>(static_cast<void*>(static_cast<char*>(p) - sizeof(block)));
153 b->next = nullptr;
154 local().push(b);
155 }
156 };
157
158 static_assert(frame_allocator<recycling_frame_allocator>);
159
160 } // namespace detail
161 } // namespace capy
162 } // namespace boost
163
164 #endif
165