GCC Code Coverage Report


Directory: ./
File: include/na64util/mem/pools.hh
Date: 2025-09-01 06:19:01
Exec Total Coverage
Lines: 48 48 100.0%
Functions: 22 28 78.6%
Branches: 15 18 83.3%

Line Branch Exec Source
1 #pragma once
2
3 /**\file
4 * \brief Memory pools used to count references across complex structures
5 *
6 * Object pools provide storage for pointers with reference counting. Reference
7 * counting decreases complexity of object creation/deletion within
8 * hierarchical data (for instance, physical event description) where same
9 * instance might be referenced within multiple levels of the hierarchy.
10 * However, this comes by cost of an additional CPU overhead for the
11 * increment/decrement operations of the reference counter for each instance
12 * being considered.
13 *
14 * These helper class is designed in hope to mitigate this possible performance
15 * impact by keeping counters close to the allocated objects, if appropriate
16 * memory allocation strategy is in use. Additional enhancement is done for
17 * arrays of controlled objects as these arrays are not re-allocated
18 * (e.g. pools are designed for reentrant usage).
19 * */
20
21 #include <vector>
22 #include <map>
23 #include <unordered_map>
24 #include <typeindex>
25 #include <memory>
26 #include <cstring>
27
28 #include <log4cpp/Category.hh>
29
30 #include "na64util/mem/alloc.hh"
31
32 #include "na64util/mem/plainBlock.hh"
33 // TODO: ... other strategies?
34
35 #include "na64util/str-fmt.hh"
36
37 namespace na64dp {
38 namespace mem {
39
40 /**\brief A base object pool class
41 *
42 * This is a basic
43 * */
44 struct AbstractPool {
45 virtual void clear() = 0;
46 10 virtual ~AbstractPool() {}
47 };
48
49 template<typename T> using Ref = std::shared_ptr<T>; // TODO decltype(create)
50
51 /**\brief A pool of shared pointers corresponding to event data structs
52 *
53 * Instance represents a set of objects of certain type associated by the mean
54 * of shared (reference counting) object (`mem::Ref<T>` that is typically a
55 * `std::shared_ptr`).
56 *
57 * Typically, used to manage objects of a current event instance (e.g. hits,
58 * an event itself, etc).
59 *
60 * Provides mechanism to remove the data that must be considered not being used
61 * by any association. For instance, noise hits that has been removed from main
62 * event's collection and do not participate in any cluster/track ossociation
63 * map will be only referenced by the pool and their refcount will be =1 (e.g.
64 * only used by pool).
65 * */
66 template< typename T //< object type
67 , typename StrategyT //< object allocation strategy type
68 //, template<typename> class ContainerAllocatorTT=std::allocator //< pool container allocator
69 , typename ContainerAllocatorT=std::allocator< Ref<T> >
70 >
71 class Pool : public AbstractPool
72 , public std::vector<Ref<T>, ContainerAllocatorT> {
73 public:
74 typedef std::vector<Ref<T>, ContainerAllocatorT> Parent;
75 private:
76 /// Allocator for objects managed by shared pointer
77 mem::Allocator<Ref<T>, StrategyT> _objAllocr;
78 public:
79 /// Ctr, dispatches allocators
80 5 Pool( mem::Allocator<Ref<T>, StrategyT> allocr
81 , ContainerAllocatorT && cntrAllocator )
82 : Parent(cntrAllocator)
83 5 , _objAllocr(allocr)
84 5 {}
85
86 /// \brief Creates new object on pool
87 ///
88 /// \returns copyable reference (smart pointer) to constructed instance
89 29 template<class... Args> Ref<T> create(Args&&... args) {
90 #if 1 // TODO: this is sub-optimal
91
1/1
✓ Branch 2 taken 29 times.
58 auto ptr = std::allocate_shared< T,
92 na64dp::mem::Allocator<T, PlainBlock> >(
93 //na64dp::mem::Allocator<T, PlainBlock>(this->get_allocator())
94 29 _objAllocr //this->get_allocator() // 1st arg of allocate_shared()
95 , std::forward<Args>(args)... // ...this relate to actual ctr
96 );
97
1/1
✓ Branch 1 taken 29 times.
29 this->push_back(ptr);
98 #else
99 this->emplace_back( Pool<T, StrategyT>(_strategy), std::forward<Args>(args)... );
100 #endif
101 58 return this->back();
102 29 }
103
104 /// Clears the objects (leaving the allocated memory intact)
105 18 virtual void clear() override {
106 18 Parent::clear();
107 18 }
108 };
109
110 template<typename ObjectT> struct PoolTraits; // undef
111
112 /**\brief A full set of pool allocators typically used together with a single
113 * reentrant event instance
114 *
115 * Instance of this class typically represents a local memory storage related
116 * to a single event processing thread. It dispatches the memory-allocation
117 * strategy to object creation, construction and destruction calls also
118 * facilitating the event object reference counting pools.
119 *
120 * Full set of event pools are provided by execution API to each handler/source
121 * together with current event (and may be retrieved from the event at any time).
122 *
123 * \todo Parameterise container allocator instance (see creation in
124 * `pool_of()`, etc). Probably this allocator(s) too may require some
125 * parameters.
126 */
127 template< typename StrategyT
128 , template<typename> class PoolContainerAllocatorT=std::allocator
129 >
130 class Pools {
131 private:
132 /// Pools referenced by unique object index (statically resolved)
133 /*static*/ AbstractPool ** _pools;
134 /// Event memory allocation strategy
135 StrategyT & _strategy;
136 public:
137
138 /// Creates a set of pools managed by current strategy instance
139 ///
140 /// \todo container allocator parameters
141 10 Pools( StrategyT & s )
142 10 : _pools(nullptr)
143 10 , _strategy(s)
144 {
145 // Use current strategy to allocate array with pool pointers
146 typedef PoolContainerAllocatorT<AbstractPool *> PoolAllocator;
147 PoolAllocator allocr; //< todo: parameters for allocator?
148 10 _pools = std::allocator_traits<PoolAllocator>::allocate( allocr
149 , NA64SW_MAX_POOLS );
150 // Nullate the pointers array
151 10 bzero( _pools, sizeof(AbstractPool*)*NA64SW_MAX_POOLS );
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 assert(_pools);
153 10 }
154
155 /// Destroys allocated pools
156 10 ~Pools() {
157 // Free pools that have been created
158 2570 for( AbstractPool ** poolPtr = _pools
159
2/2
✓ Branch 0 taken 2560 times.
✓ Branch 1 taken 10 times.
2570 ; poolPtr != _pools + NA64SW_MAX_POOLS
160 ; ++poolPtr ) {
161
2/2
✓ Branch 0 taken 2555 times.
✓ Branch 1 taken 5 times.
2560 if( ! *poolPtr ) continue; // was not created
162 typedef PoolContainerAllocatorT<AbstractPool> PoolAllocator;
163 typedef std::allocator_traits<PoolAllocator> AlTraits;
164 PoolAllocator allocr; //< todo: parameters for allocator?
165 // free the instance (though we do not know its particular type)
166 5 AlTraits::destroy( allocr, *poolPtr );
167 5 AlTraits::deallocate( allocr, *poolPtr, 1 );
168 }
169 { // free array itself
170 typedef PoolContainerAllocatorT<AbstractPool*> PoolAllocator;
171 typedef std::allocator_traits<PoolAllocator> AlTraits;
172 PoolAllocator allocr; //< todo: parameters for allocator?
173 10 AlTraits::deallocate( allocr, _pools, NA64SW_MAX_POOLS );
174 10 _pools = nullptr;
175 }
176 10 }
177
178 ///\brief Clears existing pools (typically, at the end of each event).
179 ///
180 /// Deletes the content of the pools. Since most of the pool
181 /// implementations do not actually release memory blocks associated to
182 /// the underlying container object, keeping instances saves some CPU.
183 18 void reset() {
184 4626 for( AbstractPool ** poolPtr = _pools
185
2/2
✓ Branch 0 taken 4608 times.
✓ Branch 1 taken 18 times.
4626 ; poolPtr != _pools + NA64SW_MAX_POOLS
186 ; ++poolPtr ) {
187
2/2
✓ Branch 0 taken 4590 times.
✓ Branch 1 taken 18 times.
4608 if( ! *poolPtr ) continue; // omit pools that were not created
188 18 (*poolPtr)->clear();
189 }
190 18 }
191
192 /// Returns pool of objects of certain type
193 ///
194 /// This access function creates, and/or returns reference to pool object
195 /// of certain type.
196 ///
197 /// \todo Extend it with runtime-resolved pools using SFINAE?
198 29 template<typename T> Pool<T, StrategyT> & pool_of() {
199 assert(PoolTraits<T>::id < NA64SW_MAX_POOLS);
200 #if 0 // XXX, workaround, a complicated unresolved bug
201 // The `_pools` has to be initialized once the first instance of this
202 // template is created. However, it appears to be null in certain
203 // contexts, that I could not trace even with GDB `watch`. Valgrind
204 // neither provided info.
205 //#warning "FIXME: a weird static template member behaviour"
206 if(!_pools) {
207 // Use current strategy to allocate array with pool pointers
208 typedef PoolContainerAllocatorT<AbstractPool *> PoolAllocator;
209 PoolAllocator allocr; //< todo: parameters for allocator?
210 _pools = std::allocator_traits<PoolAllocator>::allocate( allocr
211 , NA64SW_MAX_POOLS );
212 // Nullate the pointers array
213 bzero( _pools, sizeof(AbstractPool*)*NA64SW_MAX_POOLS );
214 }
215 #endif
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 assert(_pools);
217 29 AbstractPool * absPoolPtr = _pools[PoolTraits<T>::id];
218
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 24 times.
29 if( ! absPoolPtr ) {
219 // create pool
220 //typedef Allocator<Pool<T, StrategyT>, StrategyT> Allocr;
221 //typedef std::allocator_traits<Allocr> AlTraits;
222
223 typedef PoolContainerAllocatorT< Pool<T, StrategyT> > PoolAllocator;
224 typedef std::allocator_traits<PoolAllocator> AlTraits;
225 PoolAllocator allocr; //< todo: parameters for allocator?
226
227 //Allocr allocr(_strategy);
228 5 Pool<T, StrategyT> * poolPtr = AlTraits::allocate(allocr, 1);
229 AlTraits::construct( allocr // construct() arg -- allocator for ...
230 , poolPtr // allocated Pool<> instance ptr to construct on
231 10 , mem::Allocator<Ref<T>, StrategyT>(_strategy) // object allocator
232 5 , PoolContainerAllocatorT< Ref<T> >() // container allocator, TODO?
233 );
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 assert(poolPtr);
235 5 absPoolPtr = _pools[PoolTraits<T>::id] = poolPtr;
236 }
237 29 return static_cast<Pool<T, StrategyT>&>(*absPoolPtr);
238 }
239
240 /// Implicit conversion operator to allocator subtype, needed by `event` ctrs
241 630 template<typename T> operator Allocator<T, StrategyT>() { return _strategy; }
242
243 /// Instantiates pools with default parameters.
244 //static Pools<StrategyT> instantiate();
245 // Instantiates pools with default parameters.
246 //static Pools<StrategyT> instantiate( YAML:: ... );
247
248 /// Shortcut for lmem.pool_of<T>().create(lmem, ...)
249 29 template<typename T, class... Args> Ref<T> create(Args&&... args) {
250 29 return this->pool_of<T>().create(std::forward<Args>(args)...);
251 }
252 };
253
254 //template<typename StrategyT, template<typename> class PoolContainerAllocatorT>
255 //AbstractPool ** Pools<StrategyT, PoolContainerAllocatorT>::_pools = nullptr;
256
257 }
258 }
259
260