GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/ex/detail/recycling_frame_allocator.hpp
Date: 2026-01-15 23:24:40
Exec Total Coverage
Lines: 48 57 84.2%
Functions: 9 9 100.0%
Branches: 17 23 73.9%

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 2 ~global_pool()
48 {
49
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 while(head)
50 {
51 auto p = head;
52 head = head->next;
53 ::operator delete(p);
54 }
55 2 }
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 19 block* pop(std::size_t n)
65 {
66
1/1
✓ Branch 1 taken 19 times.
19 std::lock_guard<std::mutex> lock(mtx);
67 19 block** pp = &head;
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 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 19 return nullptr;
80 19 }
81 };
82
83 struct local_pool
84 {
85 block* head = nullptr;
86
87 2 ~local_pool()
88 {
89
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
21 while(head)
90 {
91 19 auto p = head;
92 19 head = head->next;
93 19 ::operator delete(p);
94 }
95 2 }
96
97 217 void push(block* b)
98 {
99 217 b->next = head;
100 217 head = b;
101 217 }
102
103 217 block* pop(std::size_t n)
104 {
105 217 block** pp = &head;
106
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 19 times.
272 while(*pp)
107 {
108 // block->size stores total allocated size (including header)
109
2/2
✓ Branch 0 taken 198 times.
✓ Branch 1 taken 55 times.
253 if((*pp)->size >= n + sizeof(block))
110 {
111 198 block* p = *pp;
112 198 *pp = p->next;
113 198 return p;
114 }
115 55 pp = &(*pp)->next;
116 }
117 19 return nullptr;
118 }
119 };
120
121 434 static local_pool& local()
122 {
123
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 432 times.
434 static thread_local local_pool local;
124 434 return local;
125 }
126
127 19 static global_pool& global()
128 {
129
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 17 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
19 static global_pool pool;
130 19 return pool;
131 }
132
133 public:
134 217 void* allocate(std::size_t n)
135 {
136 217 std::size_t total = n + sizeof(block);
137
138
2/2
✓ Branch 2 taken 198 times.
✓ Branch 3 taken 19 times.
217 if(auto* b = local().pop(n))
139 198 return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
140
141
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 if(auto* b = global().pop(n))
142 return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
143
144 19 auto* b = static_cast<block*>(::operator new(total));
145 19 b->next = nullptr;
146 19 b->size = total;
147 19 return static_cast<char*>(static_cast<void*>(b)) + sizeof(block);
148 }
149
150 217 void deallocate(void* p, std::size_t)
151 {
152 217 auto* b = static_cast<block*>(static_cast<void*>(static_cast<char*>(p) - sizeof(block)));
153 217 b->next = nullptr;
154 217 local().push(b);
155 217 }
156 };
157
158 static_assert(frame_allocator<recycling_frame_allocator>);
159
160 } // namespace detail
161 } // namespace capy
162 } // namespace boost
163
164 #endif
165