xref: /petsc/src/sys/objects/cxx/memory/object_pool.cxx (revision 017deb10d530c1b6d9744fcd772cd96c5fcd74f2)
1*27d4e10aSPierre Jolivet #include <petsc/private/cpp/object_pool.hpp>
2*27d4e10aSPierre Jolivet 
3*27d4e10aSPierre Jolivet #include <new>       // std::nothrow
4*27d4e10aSPierre Jolivet #include <limits>    // std::numeric_limits
5*27d4e10aSPierre Jolivet #include <algorithm> // std::lower_bound()
6*27d4e10aSPierre Jolivet #include <cstdio>    // std::printf
7*27d4e10aSPierre Jolivet 
8*27d4e10aSPierre Jolivet namespace Petsc
9*27d4e10aSPierre Jolivet {
10*27d4e10aSPierre Jolivet 
11*27d4e10aSPierre Jolivet namespace memory
12*27d4e10aSPierre Jolivet {
13*27d4e10aSPierre Jolivet 
14*27d4e10aSPierre Jolivet // ==========================================================================================
15*27d4e10aSPierre Jolivet // PoolAllocator -- Private API -- AllocationHeader
16*27d4e10aSPierre Jolivet //
17*27d4e10aSPierre Jolivet // The header inserted for each allocated pointer. It stores:
18*27d4e10aSPierre Jolivet //
19*27d4e10aSPierre Jolivet // - size - the size (in bytes) of the allocation. This includes ONLY the size as requested by
20*27d4e10aSPierre Jolivet //          the user. i.e. if a user requested 10 bytes but alignment, padding and header
21*27d4e10aSPierre Jolivet //          overhead results in the actual allocation being 30 bytes, then size = 10.
22*27d4e10aSPierre Jolivet // - align - the alignment (in bytes) of the allocated pointer.
23*27d4e10aSPierre Jolivet // ==========================================================================================
24*27d4e10aSPierre Jolivet 
25*27d4e10aSPierre Jolivet struct PoolAllocator::AllocationHeader {
26*27d4e10aSPierre Jolivet   constexpr AllocationHeader(size_type, align_type) noexcept;
27*27d4e10aSPierre Jolivet 
28*27d4e10aSPierre Jolivet   PETSC_NODISCARD static constexpr align_type max_alignment() noexcept;
29*27d4e10aSPierre Jolivet   PETSC_NODISCARD static constexpr size_type  header_size() noexcept;
30*27d4e10aSPierre Jolivet   PETSC_NODISCARD static constexpr size_type  buffer_zone_size() noexcept;
31*27d4e10aSPierre Jolivet 
32*27d4e10aSPierre Jolivet   size_type  size;
33*27d4e10aSPierre Jolivet   align_type align;
34*27d4e10aSPierre Jolivet };
35*27d4e10aSPierre Jolivet 
36*27d4e10aSPierre Jolivet // ==========================================================================================
37*27d4e10aSPierre Jolivet // PoolAllocator -- Private API -- AllocationHeader -- Public API
38*27d4e10aSPierre Jolivet // ==========================================================================================
39*27d4e10aSPierre Jolivet 
40*27d4e10aSPierre Jolivet /*
41*27d4e10aSPierre Jolivet   PoolAllocator::AllocationHeader::AllocationHeader
42*27d4e10aSPierre Jolivet */
AllocationHeader(size_type size,align_type align)43*27d4e10aSPierre Jolivet constexpr PoolAllocator::AllocationHeader::AllocationHeader(size_type size, align_type align) noexcept : size{size}, align{align} { }
44*27d4e10aSPierre Jolivet 
45*27d4e10aSPierre Jolivet /*
46*27d4e10aSPierre Jolivet   PoolAllocator::AllocationHeader::max_alignment
47*27d4e10aSPierre Jolivet 
48*27d4e10aSPierre Jolivet   Returns the maximum supported alignment (in bytes) of the memory pool.
49*27d4e10aSPierre Jolivet */
max_alignment()50*27d4e10aSPierre Jolivet constexpr PoolAllocator::align_type PoolAllocator::AllocationHeader::max_alignment() noexcept
51*27d4e10aSPierre Jolivet {
52*27d4e10aSPierre Jolivet #if PETSC_CPP_VERSION >= 14
53*27d4e10aSPierre Jolivet   constexpr auto max_align = std::numeric_limits<unsigned char>::max() + 1;
54*27d4e10aSPierre Jolivet   static_assert(!(max_align & (max_align - 1)), "Maximum alignment must be a power of 2");
55*27d4e10aSPierre Jolivet   return static_cast<align_type>(max_align);
56*27d4e10aSPierre Jolivet #else
57*27d4e10aSPierre Jolivet   return static_cast<align_type>(std::numeric_limits<unsigned char>::max() + 1);
58*27d4e10aSPierre Jolivet #endif
59*27d4e10aSPierre Jolivet }
60*27d4e10aSPierre Jolivet 
61*27d4e10aSPierre Jolivet /*
62*27d4e10aSPierre Jolivet   PoolAllocator::AllocationHeader::buffer_zone_size
63*27d4e10aSPierre Jolivet 
64*27d4e10aSPierre Jolivet   Notes:
65*27d4e10aSPierre Jolivet   Returns the number of bytes between the allocated pointer and the location where the
66*27d4e10aSPierre Jolivet   alignment diff is stored. i.e. size of the buffer zone + 1.
67*27d4e10aSPierre Jolivet 
68*27d4e10aSPierre Jolivet   If ASAN is enabled then this buffer zone is poisoned, so any overrun on part of the user is
69*27d4e10aSPierre Jolivet   potentially caught. The larger the buffer zone, the more likely that the user lands in
70*27d4e10aSPierre Jolivet   poisoned memory. Turned off in optimized builds.
71*27d4e10aSPierre Jolivet */
buffer_zone_size()72*27d4e10aSPierre Jolivet constexpr PoolAllocator::size_type PoolAllocator::AllocationHeader::buffer_zone_size() noexcept
73*27d4e10aSPierre Jolivet {
74*27d4e10aSPierre Jolivet   return (PetscDefined(USE_DEBUG) ? 32 : 0) + 1;
75*27d4e10aSPierre Jolivet }
76*27d4e10aSPierre Jolivet 
77*27d4e10aSPierre Jolivet /*
78*27d4e10aSPierre Jolivet   PoolAllocator::AllocationHeader::header_size
79*27d4e10aSPierre Jolivet 
80*27d4e10aSPierre Jolivet   Notes:
81*27d4e10aSPierre Jolivet   Returns the minimum size of the allocation header in bytes. Essentially (literally) the size
82*27d4e10aSPierre Jolivet   of the header object + size of the buffer zone. Does not include padding due to alignment
83*27d4e10aSPierre Jolivet   offset itself though.
84*27d4e10aSPierre Jolivet */
header_size()85*27d4e10aSPierre Jolivet constexpr PoolAllocator::size_type PoolAllocator::AllocationHeader::header_size() noexcept
86*27d4e10aSPierre Jolivet {
87*27d4e10aSPierre Jolivet   return sizeof(AllocationHeader) + buffer_zone_size();
88*27d4e10aSPierre Jolivet }
89*27d4e10aSPierre Jolivet 
90*27d4e10aSPierre Jolivet /*
91*27d4e10aSPierre Jolivet   PoolAllocator::total_size_ - Compute the maximum total size for an allocation
92*27d4e10aSPierre Jolivet 
93*27d4e10aSPierre Jolivet   Input Parameters:
94*27d4e10aSPierre Jolivet + size  - the size (in bytes) requested by the user
95*27d4e10aSPierre Jolivet - align - the alignment (in bytes) requested by the user
96*27d4e10aSPierre Jolivet 
97*27d4e10aSPierre Jolivet   Notes:
98*27d4e10aSPierre Jolivet   Returns a size so that std::malloc(total_size_(size, align)) allocates enough memory to store
99*27d4e10aSPierre Jolivet   the allocation header, buffer zone, alignment offset and requested size including any
100*27d4e10aSPierre Jolivet   potential changes due to alignment.
101*27d4e10aSPierre Jolivet */
total_size_(size_type size,align_type align)102*27d4e10aSPierre Jolivet constexpr PoolAllocator::size_type PoolAllocator::total_size_(size_type size, align_type align) noexcept
103*27d4e10aSPierre Jolivet {
104*27d4e10aSPierre Jolivet   // align - 1 because aligning the pointer up by align bytes just returns it to its original
105*27d4e10aSPierre Jolivet   // alignment, so we can save the byte
106*27d4e10aSPierre Jolivet   return AllocationHeader::header_size() + size + util::to_underlying(align) - 1;
107*27d4e10aSPierre Jolivet }
108*27d4e10aSPierre Jolivet 
109*27d4e10aSPierre Jolivet /*
110*27d4e10aSPierre Jolivet   PoolAllocator::delete_ptr_ - deletes a pointer and the corresponding header
111*27d4e10aSPierre Jolivet 
112*27d4e10aSPierre Jolivet   Input Parameter:
113*27d4e10aSPierre Jolivet + in_ptr - the pointer to user-memory which to delete
114*27d4e10aSPierre Jolivet 
115*27d4e10aSPierre Jolivet   Notes:
116*27d4e10aSPierre Jolivet   in_ptr may point to nullptr (in which case this does nothing), otherwise it must have been
117*27d4e10aSPierre Jolivet   allocated by the pool.
118*27d4e10aSPierre Jolivet 
119*27d4e10aSPierre Jolivet   in_ptr may point to poisoned memory, this routine will remove any poisoning before
120*27d4e10aSPierre Jolivet   deallocating.
121*27d4e10aSPierre Jolivet 
122*27d4e10aSPierre Jolivet   in_ptr is set to nullptr on return.
123*27d4e10aSPierre Jolivet */
delete_ptr_(void ** in_ptr)124*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::delete_ptr_(void **in_ptr) noexcept
125*27d4e10aSPierre Jolivet {
126*27d4e10aSPierre Jolivet   PetscFunctionBegin;
127*27d4e10aSPierre Jolivet   PetscAssertPointer(in_ptr, 1);
128*27d4e10aSPierre Jolivet   if (const auto ptr = util::exchange(*in_ptr, nullptr)) {
129*27d4e10aSPierre Jolivet     AllocationHeader *header = nullptr;
130*27d4e10aSPierre Jolivet 
131*27d4e10aSPierre Jolivet     PetscCall(extract_header_(ptr, &header, false));
132*27d4e10aSPierre Jolivet     // must unpoison the header itself before we can access the members
133*27d4e10aSPierre Jolivet     PetscCall(PetscUnpoisonMemoryRegion(header, sizeof(*header)));
134*27d4e10aSPierre Jolivet     PetscCall(PetscUnpoisonMemoryRegion(header, total_size_(header->size, header->align)));
135*27d4e10aSPierre Jolivet     PetscCallCXX(::delete[] reinterpret_cast<unsigned char *>(header));
136*27d4e10aSPierre Jolivet   }
137*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
138*27d4e10aSPierre Jolivet }
139*27d4e10aSPierre Jolivet 
140*27d4e10aSPierre Jolivet /*
141*27d4e10aSPierre Jolivet   PoolAllocator::find_align_ - Return an iterator to the memory pool for a particular alignment
142*27d4e10aSPierre Jolivet 
143*27d4e10aSPierre Jolivet   Input Parameter:
144*27d4e10aSPierre Jolivet . align - The alignment (in bytes) to search for
145*27d4e10aSPierre Jolivet 
146*27d4e10aSPierre Jolivet   Notes:
147*27d4e10aSPierre Jolivet   returns pool().end() if alignment not found.
148*27d4e10aSPierre Jolivet */
find_align_(align_type align)149*27d4e10aSPierre Jolivet PoolAllocator::pool_type::iterator PoolAllocator::find_align_(align_type align) noexcept
150*27d4e10aSPierre Jolivet {
151*27d4e10aSPierre Jolivet   return std::lower_bound(this->pool().begin(), this->pool().end(), align, [](const pool_type::value_type &pair, const align_type &align) { return pair.first < align; });
152*27d4e10aSPierre Jolivet }
153*27d4e10aSPierre Jolivet 
find_align_(align_type align) const154*27d4e10aSPierre Jolivet PoolAllocator::pool_type::const_iterator PoolAllocator::find_align_(align_type align) const noexcept
155*27d4e10aSPierre Jolivet {
156*27d4e10aSPierre Jolivet   return std::lower_bound(this->pool().begin(), this->pool().end(), align, [](const pool_type::value_type &pair, const align_type &align) { return pair.first < align; });
157*27d4e10aSPierre Jolivet }
158*27d4e10aSPierre Jolivet 
159*27d4e10aSPierre Jolivet /*
160*27d4e10aSPierre Jolivet   PoolAllocator::clear_ - Clear the memory pool
161*27d4e10aSPierre Jolivet 
162*27d4e10aSPierre Jolivet   Output Parameter:
163*27d4e10aSPierre Jolivet . remaining - The number of remaining allocations in the pool, nullptr if not needed
164*27d4e10aSPierre Jolivet 
165*27d4e10aSPierre Jolivet   Notes:
166*27d4e10aSPierre Jolivet   This will clean up the pool, deallocating any memory checked back into the pool. This does
167*27d4e10aSPierre Jolivet   not delete allocations that were allocated by the pool but not yet returned to it.
168*27d4e10aSPierre Jolivet 
169*27d4e10aSPierre Jolivet   remaining is useful in determining if any allocations were "leaked". Suppose an object
170*27d4e10aSPierre Jolivet   internally manages a resource and uses the pool to allocate said resource. On destruction the
171*27d4e10aSPierre Jolivet   object expects to have the pool be empty, i.e. have remaining = 0. This implies all resources
172*27d4e10aSPierre Jolivet   were returned to the pool.
173*27d4e10aSPierre Jolivet */
clear_(size_type * remaining)174*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::clear_(size_type *remaining) noexcept
175*27d4e10aSPierre Jolivet {
176*27d4e10aSPierre Jolivet   size_type remain = 0;
177*27d4e10aSPierre Jolivet 
178*27d4e10aSPierre Jolivet   PetscFunctionBegin;
179*27d4e10aSPierre Jolivet   if (remaining) PetscAssertPointer(remaining, 1);
180*27d4e10aSPierre Jolivet   // clang-format off
181*27d4e10aSPierre Jolivet   PetscCall(
182*27d4e10aSPierre Jolivet     this->for_each([&](void *&ptr)
183*27d4e10aSPierre Jolivet     {
184*27d4e10aSPierre Jolivet       PetscFunctionBegin;
185*27d4e10aSPierre Jolivet       ++remain;
186*27d4e10aSPierre Jolivet       PetscCall(delete_ptr_(&ptr));
187*27d4e10aSPierre Jolivet       PetscFunctionReturn(PETSC_SUCCESS);
188*27d4e10aSPierre Jolivet     })
189*27d4e10aSPierre Jolivet   );
190*27d4e10aSPierre Jolivet   // clang-format on
191*27d4e10aSPierre Jolivet   PetscCallCXX(this->pool().clear());
192*27d4e10aSPierre Jolivet   if (remaining) *remaining = remain;
193*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
194*27d4e10aSPierre Jolivet }
195*27d4e10aSPierre Jolivet 
196*27d4e10aSPierre Jolivet /*
197*27d4e10aSPierre Jolivet   PoolAllocator::finalize_ - Routine automatically called during PetscFinalize()
198*27d4e10aSPierre Jolivet 
199*27d4e10aSPierre Jolivet   Notes:
200*27d4e10aSPierre Jolivet   This will go through and reap any regions that it owns in the underlying container. If it
201*27d4e10aSPierre Jolivet   owns all regions, it resets the container.
202*27d4e10aSPierre Jolivet 
203*27d4e10aSPierre Jolivet   There currently is no way to ensure that objects remaining in the pool aren't leaked, since
204*27d4e10aSPierre Jolivet   this routine cannot actually re-register the pool for finalizations without causing an
205*27d4e10aSPierre Jolivet   infinite loop...
206*27d4e10aSPierre Jolivet 
207*27d4e10aSPierre Jolivet   Thus it is up to the owned object to ensure that the pool is properly finalized.
208*27d4e10aSPierre Jolivet */
finalize_()209*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::finalize_() noexcept
210*27d4e10aSPierre Jolivet {
211*27d4e10aSPierre Jolivet   PetscFunctionBegin;
212*27d4e10aSPierre Jolivet   PetscCall(clear_());
213*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
214*27d4e10aSPierre Jolivet }
215*27d4e10aSPierre Jolivet 
216*27d4e10aSPierre Jolivet // a quick sanity check that the alignment is valid, does nothing in optimized builds
valid_alignment_(align_type in_align)217*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::valid_alignment_(align_type in_align) noexcept
218*27d4e10aSPierre Jolivet {
219*27d4e10aSPierre Jolivet   constexpr auto max_align = util::to_underlying(AllocationHeader::max_alignment());
220*27d4e10aSPierre Jolivet   const auto     align     = util::to_underlying(in_align);
221*27d4e10aSPierre Jolivet 
222*27d4e10aSPierre Jolivet   PetscFunctionBegin;
223*27d4e10aSPierre Jolivet   PetscAssert((align > 0) && (align <= max_align), PETSC_COMM_SELF, PETSC_ERR_MEMC, "Alignment %zu must be (0, %zu]", align, max_align);
224*27d4e10aSPierre Jolivet   PetscAssert(!(align & (align - 1)), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Alignment %zu must be a power of 2", align);
225*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
226*27d4e10aSPierre Jolivet }
227*27d4e10aSPierre Jolivet 
228*27d4e10aSPierre Jolivet /*
229*27d4e10aSPierre Jolivet   PoolAllocator::extract_header_ - Extract the header pointer from the aligned pointer
230*27d4e10aSPierre Jolivet 
231*27d4e10aSPierre Jolivet   Input Parameters:
232*27d4e10aSPierre Jolivet + user_ptr     - the pointer to the aligned user memory
233*27d4e10aSPierre Jolivet - check_in_ptr - whether to test the validity of aligned_ptr
234*27d4e10aSPierre Jolivet 
235*27d4e10aSPierre Jolivet   Output Parameter:
236*27d4e10aSPierre Jolivet . header - the pointer to the header
237*27d4e10aSPierre Jolivet 
238*27d4e10aSPierre Jolivet   Notes:
239*27d4e10aSPierre Jolivet   Setting check_in_ptr to false disabled the PetscAssertPointer() check in the function
240*27d4e10aSPierre Jolivet   preamble. This allows the method to be used even if the aligned_ptr is poisoned (for example
241*27d4e10aSPierre Jolivet   when extracting the header from a pointer that is checked into the pool).
242*27d4e10aSPierre Jolivet 
243*27d4e10aSPierre Jolivet   aligned_ptr must have been allocated by the pool.
244*27d4e10aSPierre Jolivet 
245*27d4e10aSPierre Jolivet   The returned header is still poisoned, the user is responsible for unpoisoning it.
246*27d4e10aSPierre Jolivet */
extract_header_(void * user_ptr,AllocationHeader ** header,bool check_in_ptr)247*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::extract_header_(void *user_ptr, AllocationHeader **header, bool check_in_ptr) noexcept
248*27d4e10aSPierre Jolivet {
249*27d4e10aSPierre Jolivet   PetscFunctionBegin;
250*27d4e10aSPierre Jolivet   if (check_in_ptr) PetscAssertPointer(user_ptr, 1);
251*27d4e10aSPierre Jolivet   PetscAssertPointer(header, 2);
252*27d4e10aSPierre Jolivet   {
253*27d4e10aSPierre Jolivet     //       AllocationHeader::alignment_offset() (at least 1)
254*27d4e10aSPierre Jolivet     //                        |
255*27d4e10aSPierre Jolivet     // header                 |  user_ptr/aligned_ptr
256*27d4e10aSPierre Jolivet     // |                      |          |
257*27d4e10aSPierre Jolivet     // v                  v~~~~~~~~~~~~~~v
258*27d4e10aSPierre Jolivet     // A==============B===C==============D--------------------- ...
259*27d4e10aSPierre Jolivet     // ^~~~~~~~~~~~~~~^^~~^              ^~~~~~~~~~~~~~~~~~~~~~ ...
260*27d4e10aSPierre Jolivet     //       |             \______             user memory
261*27d4e10aSPierre Jolivet     //       |                    buffer_zone_end
262*27d4e10aSPierre Jolivet     // sizeof(AllocationHeader)
263*27d4e10aSPierre Jolivet     //
264*27d4e10aSPierre Jolivet     // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
265*27d4e10aSPierre Jolivet     //                poisoned
266*27d4e10aSPierre Jolivet     //
267*27d4e10aSPierre Jolivet     const auto aligned_ptr     = reinterpret_cast<unsigned char *>(user_ptr);
268*27d4e10aSPierre Jolivet     const auto buffer_zone_end = aligned_ptr - AllocationHeader::buffer_zone_size();
269*27d4e10aSPierre Jolivet 
270*27d4e10aSPierre Jolivet     PetscCall(PetscUnpoisonMemoryRegion(buffer_zone_end, sizeof(*buffer_zone_end)));
271*27d4e10aSPierre Jolivet     {
272*27d4e10aSPierre Jolivet       // offset added to original pointer due to alignment, B -> C above (may be zero)
273*27d4e10aSPierre Jolivet       const auto alignment_offset = *buffer_zone_end;
274*27d4e10aSPierre Jolivet 
275*27d4e10aSPierre Jolivet       *header = reinterpret_cast<AllocationHeader *>(buffer_zone_end - alignment_offset - sizeof(AllocationHeader));
276*27d4e10aSPierre Jolivet     }
277*27d4e10aSPierre Jolivet     PetscCall(PetscPoisonMemoryRegion(buffer_zone_end, sizeof(*buffer_zone_end)));
278*27d4e10aSPierre Jolivet   }
279*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
280*27d4e10aSPierre Jolivet }
281*27d4e10aSPierre Jolivet 
282*27d4e10aSPierre Jolivet /*
283*27d4e10aSPierre Jolivet   PoolAllocator::allocate_ptr_ - Allocate a pointer and header given requested size and
284*27d4e10aSPierre Jolivet   alignment
285*27d4e10aSPierre Jolivet 
286*27d4e10aSPierre Jolivet   Input Parameters:
287*27d4e10aSPierre Jolivet + size  - the size (in bytes) to allocate
288*27d4e10aSPierre Jolivet - align - the size (in bytes) to align the pointer to
289*27d4e10aSPierre Jolivet 
290*27d4e10aSPierre Jolivet   Output Parameter:
291*27d4e10aSPierre Jolivet . ret_ptr - the resulting pointer to user-memory
292*27d4e10aSPierre Jolivet 
293*27d4e10aSPierre Jolivet   Notes:
294*27d4e10aSPierre Jolivet   Both size and align must be > 0. align must be a power of 2.
295*27d4e10aSPierre Jolivet   This both allocates the user memory and the corresponding metadata region.
296*27d4e10aSPierre Jolivet */
allocate_ptr_(size_type size,align_type align,void ** ret_ptr)297*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::allocate_ptr_(size_type size, align_type align, void **ret_ptr) noexcept
298*27d4e10aSPierre Jolivet {
299*27d4e10aSPierre Jolivet   constexpr auto header_size = AllocationHeader::header_size();
300*27d4e10aSPierre Jolivet   const auto     total_size  = total_size_(size, align);
301*27d4e10aSPierre Jolivet   const auto     size_before = total_size - header_size;
302*27d4e10aSPierre Jolivet   auto           usable_size = size_before;
303*27d4e10aSPierre Jolivet   void          *aligned_ptr = nullptr;
304*27d4e10aSPierre Jolivet   unsigned char *base_ptr    = nullptr;
305*27d4e10aSPierre Jolivet 
306*27d4e10aSPierre Jolivet   PetscFunctionBegin;
307*27d4e10aSPierre Jolivet   PetscAssertPointer(ret_ptr, 1);
308*27d4e10aSPierre Jolivet   PetscCall(valid_alignment_(align));
309*27d4e10aSPierre Jolivet   // memory is laid out as follows:
310*27d4e10aSPierre Jolivet   //
311*27d4e10aSPierre Jolivet   //                            aligned_ptr     ret_ptr (and aligned_ptr after std::align())
312*27d4e10aSPierre Jolivet   // base_ptr     buffer_zone       |     _____/
313*27d4e10aSPierre Jolivet   // |                      |       |    /     user memory
314*27d4e10aSPierre Jolivet   // v                  v~~~~~~~~~~~x~~~v~~~~~~~~~~~~~~~~~~~~~ ...
315*27d4e10aSPierre Jolivet   // A==============B===C===========D===E--------------------- ...
316*27d4e10aSPierre Jolivet   // ^~~~~~~~~~~~~~~^^~~^
317*27d4e10aSPierre Jolivet   //        |            \_________
318*27d4e10aSPierre Jolivet   // sizeof(AllocationHeader)      |
319*27d4e10aSPierre Jolivet   //                               alignment_offset (may be 0)
320*27d4e10aSPierre Jolivet   //
321*27d4e10aSPierre Jolivet   // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
322*27d4e10aSPierre Jolivet   //                poisoned
323*27d4e10aSPierre Jolivet   //                                ^~~~~~~~~~~~~~~~~~~~~~~~~~ ...
324*27d4e10aSPierre Jolivet   //                                          usable_size
325*27d4e10aSPierre Jolivet   // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ...
326*27d4e10aSPierre Jolivet   //                       total_size_()
327*27d4e10aSPierre Jolivet   //
328*27d4e10aSPierre Jolivet   base_ptr = ::new (std::nothrow) unsigned char[total_size];
329*27d4e10aSPierre Jolivet   PetscAssert(base_ptr, PETSC_COMM_SELF, PETSC_ERR_MEM, "operator new() failed to allocate %zu bytes", total_size);
330*27d4e10aSPierre Jolivet   PetscCallCXX(base_ptr = reinterpret_cast<unsigned char *>(util::construct_at(reinterpret_cast<AllocationHeader *>(base_ptr), size, align)));
331*27d4e10aSPierre Jolivet   aligned_ptr = base_ptr + header_size;
332*27d4e10aSPierre Jolivet   // storing to ret_ptr and not aligned_ptr is deliberate! std::align() returns nullptr if it
333*27d4e10aSPierre Jolivet   // fails, so we do not want to clobber aligned_ptr
334*27d4e10aSPierre Jolivet   *ret_ptr = std::align(util::to_underlying(align), size, aligned_ptr, usable_size);
335*27d4e10aSPierre Jolivet   // note usable_size is has now shrunk by alignment_offset
336*27d4e10aSPierre Jolivet   PetscAssert(*ret_ptr, PETSC_COMM_SELF, PETSC_ERR_LIB, "std::align() failed to align pointer %p (size %zu, alignment %zu)", aligned_ptr, size, util::to_underlying(align));
337*27d4e10aSPierre Jolivet   {
338*27d4e10aSPierre Jolivet     constexpr auto max_align        = util::to_underlying(AllocationHeader::max_alignment());
339*27d4e10aSPierre Jolivet     const auto     alignment_offset = size_before - usable_size;
340*27d4e10aSPierre Jolivet 
341*27d4e10aSPierre Jolivet     PetscAssert(alignment_offset <= max_align, PETSC_COMM_SELF, PETSC_ERR_MEMC, "Computed alignment offset %zu > maximum allowed alignment %zu", alignment_offset, max_align);
342*27d4e10aSPierre Jolivet     *(reinterpret_cast<unsigned char *>(aligned_ptr) - AllocationHeader::buffer_zone_size()) = static_cast<unsigned char>(alignment_offset);
343*27d4e10aSPierre Jolivet     if (PetscDefined(USE_DEBUG)) {
344*27d4e10aSPierre Jolivet       const auto computed_aligned_ptr = base_ptr + header_size + alignment_offset;
345*27d4e10aSPierre Jolivet 
346*27d4e10aSPierre Jolivet       PetscCheck(computed_aligned_ptr == aligned_ptr, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Base pointer %p + header size %zu + alignment offset %zu = %p != aligned pointer %p", static_cast<void *>(base_ptr), header_size, alignment_offset, static_cast<void *>(computed_aligned_ptr), aligned_ptr);
347*27d4e10aSPierre Jolivet     }
348*27d4e10aSPierre Jolivet   }
349*27d4e10aSPierre Jolivet   // Poison the entire region first, then unpoison only the user region. This ensures that
350*27d4e10aSPierre Jolivet   // any extra space on *either* ends of the array are poisoned
351*27d4e10aSPierre Jolivet   PetscCall(PetscPoisonMemoryRegion(base_ptr, total_size));
352*27d4e10aSPierre Jolivet   PetscCall(PetscUnpoisonMemoryRegion(aligned_ptr, size));
353*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
354*27d4e10aSPierre Jolivet }
355*27d4e10aSPierre Jolivet 
356*27d4e10aSPierre Jolivet // ==========================================================================================
357*27d4e10aSPierre Jolivet // PoolAllocator -- Public API
358*27d4e10aSPierre Jolivet // ==========================================================================================
359*27d4e10aSPierre Jolivet 
~PoolAllocator()360*27d4e10aSPierre Jolivet PoolAllocator::~PoolAllocator() noexcept
361*27d4e10aSPierre Jolivet {
362*27d4e10aSPierre Jolivet   size_type leaked{};
363*27d4e10aSPierre Jolivet   PetscBool init;
364*27d4e10aSPierre Jolivet 
365*27d4e10aSPierre Jolivet   PetscFunctionBegin;
366*27d4e10aSPierre Jolivet   PetscCallAbort(PETSC_COMM_SELF, clear_(&leaked));
367*27d4e10aSPierre Jolivet   PetscCallAbort(PETSC_COMM_SELF, PetscInitialized(&init));
368*27d4e10aSPierre Jolivet   if (init) PetscCheckAbort(leaked == 0, PETSC_COMM_SELF, PETSC_ERR_MEM_LEAK, "%zu objects remaining in the pool are leaked", leaked);
369*27d4e10aSPierre Jolivet   PetscFunctionReturnVoid();
370*27d4e10aSPierre Jolivet }
371*27d4e10aSPierre Jolivet 
372*27d4e10aSPierre Jolivet /*
373*27d4e10aSPierre Jolivet   PoolAllocator::get_attributes - Get the size and alignment of an allocated pointer
374*27d4e10aSPierre Jolivet 
375*27d4e10aSPierre Jolivet   Input Parameter:
376*27d4e10aSPierre Jolivet . ptr - the pointer to query
377*27d4e10aSPierre Jolivet 
378*27d4e10aSPierre Jolivet   Output Parameters:
379*27d4e10aSPierre Jolivet + size - the size (in bytes) of the allocated area, nullptr if not needed
380*27d4e10aSPierre Jolivet - align - the alignment (in bytes) of the allocated, nullptr if not needed
381*27d4e10aSPierre Jolivet 
382*27d4e10aSPierre Jolivet   Note:
383*27d4e10aSPierre Jolivet   ptr must have been allocated by the pool, and is exactly the pointer returned by either
384*27d4e10aSPierre Jolivet   allocate() or try_allocate() (if successful).
385*27d4e10aSPierre Jolivet */
get_attributes(const void * ptr,size_type * size,align_type * align)386*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::get_attributes(const void *ptr, size_type *size, align_type *align) noexcept
387*27d4e10aSPierre Jolivet {
388*27d4e10aSPierre Jolivet   PetscFunctionBegin;
389*27d4e10aSPierre Jolivet   // ptr may be poisoned, so cannot check it here
390*27d4e10aSPierre Jolivet   // PetscAssertPointer(out_ptr, 1);
391*27d4e10aSPierre Jolivet   if (size) PetscAssertPointer(size, 2);
392*27d4e10aSPierre Jolivet   if (align) PetscAssertPointer(align, 3);
393*27d4e10aSPierre Jolivet   if (PetscLikely(size || align)) {
394*27d4e10aSPierre Jolivet     AllocationHeader *header = nullptr;
395*27d4e10aSPierre Jolivet 
396*27d4e10aSPierre Jolivet     PetscCall(extract_header_(const_cast<void *>(ptr), &header, /* check ptr = */ false));
397*27d4e10aSPierre Jolivet     PetscCall(PetscUnpoisonMemoryRegion(header, sizeof(*header)));
398*27d4e10aSPierre Jolivet     if (size) *size = header->size;
399*27d4e10aSPierre Jolivet     if (align) *align = header->align;
400*27d4e10aSPierre Jolivet     PetscCall(PetscPoisonMemoryRegion(header, sizeof(*header)));
401*27d4e10aSPierre Jolivet   }
402*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
403*27d4e10aSPierre Jolivet }
404*27d4e10aSPierre Jolivet 
405*27d4e10aSPierre Jolivet /*
406*27d4e10aSPierre Jolivet   PoolAllocator::try_allocate - Attempt to allocate memory from the pool
407*27d4e10aSPierre Jolivet 
408*27d4e10aSPierre Jolivet   Input Parameter:
409*27d4e10aSPierre Jolivet + size  - the size (in bytes) to attempt to allocate
410*27d4e10aSPierre Jolivet - align - the alignment (in bytes) of the requested allocation
411*27d4e10aSPierre Jolivet 
412*27d4e10aSPierre Jolivet   Output Parameters:
413*27d4e10aSPierre Jolivet + out_ptr - the pointer to return the allocated memory in
414*27d4e10aSPierre Jolivet - success - set to true if out_ptr was successfully is allocated
415*27d4e10aSPierre Jolivet 
416*27d4e10aSPierre Jolivet   Notes:
417*27d4e10aSPierre Jolivet   Differs from allocate() insofar that this routine does not allocate new memory if it does not
418*27d4e10aSPierre Jolivet   find a suitable memory chunk in the pool.
419*27d4e10aSPierre Jolivet 
420*27d4e10aSPierre Jolivet   align must be a power of 2, and > 0.
421*27d4e10aSPierre Jolivet 
422*27d4e10aSPierre Jolivet   If size is 0, out_ptr is set to nullptr and success set to false
423*27d4e10aSPierre Jolivet */
try_allocate(void ** out_ptr,size_type size,align_type align,bool * success)424*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::try_allocate(void **out_ptr, size_type size, align_type align, bool *success) noexcept
425*27d4e10aSPierre Jolivet {
426*27d4e10aSPierre Jolivet   void *ptr{};
427*27d4e10aSPierre Jolivet   bool  found{};
428*27d4e10aSPierre Jolivet 
429*27d4e10aSPierre Jolivet   PetscFunctionBegin;
430*27d4e10aSPierre Jolivet   PetscAssertPointer(out_ptr, 1);
431*27d4e10aSPierre Jolivet   PetscAssertPointer(success, 3);
432*27d4e10aSPierre Jolivet   PetscCall(valid_alignment_(align));
433*27d4e10aSPierre Jolivet   PetscCall(this->register_finalize());
434*27d4e10aSPierre Jolivet   if (PetscLikely(size)) {
435*27d4e10aSPierre Jolivet     const auto align_it = find_align_(align);
436*27d4e10aSPierre Jolivet 
437*27d4e10aSPierre Jolivet     if (align_it != this->pool().end() && align_it->first == align) {
438*27d4e10aSPierre Jolivet       auto     &&size_map = align_it->second;
439*27d4e10aSPierre Jolivet       const auto size_it  = size_map.find(size);
440*27d4e10aSPierre Jolivet 
441*27d4e10aSPierre Jolivet       if (size_it != size_map.end()) {
442*27d4e10aSPierre Jolivet         auto &&ptr_list = size_it->second;
443*27d4e10aSPierre Jolivet 
444*27d4e10aSPierre Jolivet         if (!ptr_list.empty()) {
445*27d4e10aSPierre Jolivet           found = true;
446*27d4e10aSPierre Jolivet           ptr   = ptr_list.back();
447*27d4e10aSPierre Jolivet           PetscCallCXX(ptr_list.pop_back());
448*27d4e10aSPierre Jolivet           PetscCall(PetscUnpoisonMemoryRegion(ptr, size));
449*27d4e10aSPierre Jolivet         }
450*27d4e10aSPierre Jolivet       }
451*27d4e10aSPierre Jolivet     }
452*27d4e10aSPierre Jolivet   }
453*27d4e10aSPierre Jolivet   *out_ptr = ptr;
454*27d4e10aSPierre Jolivet   *success = found;
455*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
456*27d4e10aSPierre Jolivet }
457*27d4e10aSPierre Jolivet 
458*27d4e10aSPierre Jolivet /*
459*27d4e10aSPierre Jolivet   PoolAllocator::allocate - Allocate a chunk of memory from the pool
460*27d4e10aSPierre Jolivet 
461*27d4e10aSPierre Jolivet   Input Parameters:
462*27d4e10aSPierre Jolivet + size  - The size (in bytes) to allocate
463*27d4e10aSPierre Jolivet - align - The alignment (in bytes) to align the allocation to
464*27d4e10aSPierre Jolivet 
465*27d4e10aSPierre Jolivet   Output Parameters:
466*27d4e10aSPierre Jolivet + out_ptr             - A pointer containing the beginning of the allocated region
467*27d4e10aSPierre Jolivet - allocated_from_pool - True if the region was allocated from the pool, false otherwise
468*27d4e10aSPierre Jolivet 
469*27d4e10aSPierre Jolivet   Notes:
470*27d4e10aSPierre Jolivet   If size is 0, out_ptr is set to nullptr and was_allocated is set to false.
471*27d4e10aSPierre Jolivet */
allocate(void ** out_ptr,size_type size,align_type align,bool * allocated_from_pool)472*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::allocate(void **out_ptr, size_type size, align_type align, bool *allocated_from_pool) noexcept
473*27d4e10aSPierre Jolivet {
474*27d4e10aSPierre Jolivet   bool success{};
475*27d4e10aSPierre Jolivet 
476*27d4e10aSPierre Jolivet   PetscFunctionBegin;
477*27d4e10aSPierre Jolivet   PetscAssertPointer(out_ptr, 1);
478*27d4e10aSPierre Jolivet   if (allocated_from_pool) PetscAssertPointer(allocated_from_pool, 3);
479*27d4e10aSPierre Jolivet   PetscCall(try_allocate(out_ptr, size, align, &success));
480*27d4e10aSPierre Jolivet   if (!success) PetscCall(allocate_ptr_(size, align, out_ptr));
481*27d4e10aSPierre Jolivet   if (allocated_from_pool) *allocated_from_pool = success;
482*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
483*27d4e10aSPierre Jolivet }
484*27d4e10aSPierre Jolivet 
485*27d4e10aSPierre Jolivet /*
486*27d4e10aSPierre Jolivet   PoolAllocate::deallocate - Return a pointer to the pool
487*27d4e10aSPierre Jolivet 
488*27d4e10aSPierre Jolivet   Input Parameter:
489*27d4e10aSPierre Jolivet . in_ptr - A pointer to the beginning of the allocated region
490*27d4e10aSPierre Jolivet 
491*27d4e10aSPierre Jolivet   Notes:
492*27d4e10aSPierre Jolivet   On success the region pointed to by in_ptr is poisoned. Any further attempts to access
493*27d4e10aSPierre Jolivet   the memory pointed to by in_ptr will result in an error.
494*27d4e10aSPierre Jolivet 
495*27d4e10aSPierre Jolivet   in_ptr must have been allocated by the pool, and must point to the beginning of the allocated
496*27d4e10aSPierre Jolivet   region.
497*27d4e10aSPierre Jolivet 
498*27d4e10aSPierre Jolivet   The value in_ptr points to may be nullptr, in which case this routine does nothing.
499*27d4e10aSPierre Jolivet */
deallocate(void ** in_ptr,size_type size,align_type align)500*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::deallocate(void **in_ptr, size_type size, align_type align) noexcept
501*27d4e10aSPierre Jolivet {
502*27d4e10aSPierre Jolivet   PetscFunctionBegin;
503*27d4e10aSPierre Jolivet   PetscAssertPointer(in_ptr, 1);
504*27d4e10aSPierre Jolivet   if (auto ptr = util::exchange(*in_ptr, nullptr)) {
505*27d4e10aSPierre Jolivet     if (locked_) {
506*27d4e10aSPierre Jolivet       // This is necessary if an object is "reclaimed" within another PetscFinalize()
507*27d4e10aSPierre Jolivet       // registered cleanup after this pool has returned from its finalizer. In this case,
508*27d4e10aSPierre Jolivet       // instead of pushing onto the stack we just delete the pointer directly.
509*27d4e10aSPierre Jolivet       //
510*27d4e10aSPierre Jolivet       // However this path is *only* valid if we have already finalized!
511*27d4e10aSPierre Jolivet       PetscCall(delete_ptr_(&ptr));
512*27d4e10aSPierre Jolivet     } else {
513*27d4e10aSPierre Jolivet       auto it = find_align_(align);
514*27d4e10aSPierre Jolivet 
515*27d4e10aSPierre Jolivet       if (it == this->pool().end() || it->first != align) PetscCallCXX(it = this->pool().insert(it, {align, {}}));
516*27d4e10aSPierre Jolivet       PetscCallCXX(it->second[size].emplace_back(ptr));
517*27d4e10aSPierre Jolivet       PetscCall(PetscPoisonMemoryRegion(ptr, size));
518*27d4e10aSPierre Jolivet     }
519*27d4e10aSPierre Jolivet   }
520*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
521*27d4e10aSPierre Jolivet }
522*27d4e10aSPierre Jolivet 
523*27d4e10aSPierre Jolivet /*
524*27d4e10aSPierre Jolivet   PoolAllocator::unpoison - Unpoison a pool-allocated pointer
525*27d4e10aSPierre Jolivet 
526*27d4e10aSPierre Jolivet   Input Parameter:
527*27d4e10aSPierre Jolivet . ptr - the pointer to poison
528*27d4e10aSPierre Jolivet 
529*27d4e10aSPierre Jolivet   Output Parameter:
530*27d4e10aSPierre Jolivet . size - the size (in bytes) of the region pointed to by ptr
531*27d4e10aSPierre Jolivet 
532*27d4e10aSPierre Jolivet   Notes:
533*27d4e10aSPierre Jolivet   ptr must not be nullptr.
534*27d4e10aSPierre Jolivet 
535*27d4e10aSPierre Jolivet   size should be passed to the corresponding repoison() to undo the effects of this
536*27d4e10aSPierre Jolivet   routine.
537*27d4e10aSPierre Jolivet 
538*27d4e10aSPierre Jolivet   Using this routine in conjunction with unpoison() allows a user to temporarily push and pop
539*27d4e10aSPierre Jolivet   the poisoning state of a given pointer. The pool does not repoison the pointer for you, so
540*27d4e10aSPierre Jolivet   use at your own risk!
541*27d4e10aSPierre Jolivet */
unpoison(const void * ptr,size_type * size)542*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::unpoison(const void *ptr, size_type *size) noexcept
543*27d4e10aSPierre Jolivet {
544*27d4e10aSPierre Jolivet   PetscFunctionBegin;
545*27d4e10aSPierre Jolivet   // ptr may be poisoned, so cannot check it here
546*27d4e10aSPierre Jolivet   // PetscAssertPointer(ptr, 1);
547*27d4e10aSPierre Jolivet   PetscAssertPointer(size, 2);
548*27d4e10aSPierre Jolivet   PetscCall(get_attributes(ptr, size, nullptr));
549*27d4e10aSPierre Jolivet   PetscCall(PetscUnpoisonMemoryRegion(ptr, *size));
550*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
551*27d4e10aSPierre Jolivet }
552*27d4e10aSPierre Jolivet 
553*27d4e10aSPierre Jolivet /*
554*27d4e10aSPierre Jolivet   PoolAllocator::repoison - Poison a pointer previously unpoisoned via unpoison()
555*27d4e10aSPierre Jolivet 
556*27d4e10aSPierre Jolivet   Input Parameters:
557*27d4e10aSPierre Jolivet + ptr  - the pointer to the unpoisoned region
558*27d4e10aSPierre Jolivet - size - the size of the region
559*27d4e10aSPierre Jolivet 
560*27d4e10aSPierre Jolivet   Notes:
561*27d4e10aSPierre Jolivet   size must be exactly the value returned by unpoison().
562*27d4e10aSPierre Jolivet 
563*27d4e10aSPierre Jolivet   ptr cannot be nullptr
564*27d4e10aSPierre Jolivet */
repoison(const void * ptr,size_type size)565*27d4e10aSPierre Jolivet PetscErrorCode PoolAllocator::repoison(const void *ptr, size_type size) noexcept
566*27d4e10aSPierre Jolivet {
567*27d4e10aSPierre Jolivet   PetscFunctionBegin;
568*27d4e10aSPierre Jolivet   PetscAssertPointer(ptr, 1);
569*27d4e10aSPierre Jolivet   PetscCall(PetscPoisonMemoryRegion(ptr, size));
570*27d4e10aSPierre Jolivet   PetscFunctionReturn(PETSC_SUCCESS);
571*27d4e10aSPierre Jolivet }
572*27d4e10aSPierre Jolivet 
lock_guard()573*27d4e10aSPierre Jolivet PoolAllocator::LockGuard PoolAllocator::lock_guard() noexcept
574*27d4e10aSPierre Jolivet {
575*27d4e10aSPierre Jolivet   return LockGuard{this};
576*27d4e10aSPierre Jolivet }
577*27d4e10aSPierre Jolivet 
578*27d4e10aSPierre Jolivet // ==========================================================================================
579*27d4e10aSPierre Jolivet // PoolAllocated -- Public API
580*27d4e10aSPierre Jolivet // ==========================================================================================
581*27d4e10aSPierre Jolivet 
operator new(size_type size)582*27d4e10aSPierre Jolivet void *PoolAllocated::operator new(size_type size) noexcept
583*27d4e10aSPierre Jolivet {
584*27d4e10aSPierre Jolivet   void *ptr{};
585*27d4e10aSPierre Jolivet 
586*27d4e10aSPierre Jolivet   PetscFunctionBegin;
587*27d4e10aSPierre Jolivet   PetscCallAbort(PETSC_COMM_SELF, pool().allocate(&ptr, size, static_cast<align_type>(alignof(std::max_align_t))));
588*27d4e10aSPierre Jolivet   PetscFunctionReturn(ptr);
589*27d4e10aSPierre Jolivet }
590*27d4e10aSPierre Jolivet 
operator delete(void * ptr)591*27d4e10aSPierre Jolivet void PoolAllocated::operator delete(void *ptr) noexcept
592*27d4e10aSPierre Jolivet {
593*27d4e10aSPierre Jolivet   PetscFunctionBegin;
594*27d4e10aSPierre Jolivet   if (PetscLikely(ptr)) {
595*27d4e10aSPierre Jolivet     size_type       size{};
596*27d4e10aSPierre Jolivet     align_type      align{};
597*27d4e10aSPierre Jolivet     allocator_type &allocated = pool();
598*27d4e10aSPierre Jolivet 
599*27d4e10aSPierre Jolivet     PetscCallAbort(PETSC_COMM_SELF, allocated.get_attributes(ptr, &size, &align));
600*27d4e10aSPierre Jolivet     PetscCallAbort(PETSC_COMM_SELF, allocated.deallocate(&ptr, size, align));
601*27d4e10aSPierre Jolivet   }
602*27d4e10aSPierre Jolivet   PetscFunctionReturnVoid();
603*27d4e10aSPierre Jolivet }
604*27d4e10aSPierre Jolivet 
605*27d4e10aSPierre Jolivet #if PETSC_CPP_VERSION >= 17
operator new(size_type size,std::align_val_t align)606*27d4e10aSPierre Jolivet void *PoolAllocated::operator new(size_type size, std::align_val_t align) noexcept
607*27d4e10aSPierre Jolivet {
608*27d4e10aSPierre Jolivet   void *ptr{};
609*27d4e10aSPierre Jolivet 
610*27d4e10aSPierre Jolivet   PetscFunctionBegin;
611*27d4e10aSPierre Jolivet   PetscCallAbort(PETSC_COMM_SELF, pool().allocate(&ptr, size, static_cast<align_type>(align)));
612*27d4e10aSPierre Jolivet   PetscFunctionReturn(ptr);
613*27d4e10aSPierre Jolivet }
614*27d4e10aSPierre Jolivet 
operator delete(void * ptr,std::align_val_t align)615*27d4e10aSPierre Jolivet void PoolAllocated::operator delete(void *ptr, std::align_val_t align) noexcept
616*27d4e10aSPierre Jolivet {
617*27d4e10aSPierre Jolivet   PetscFunctionBegin;
618*27d4e10aSPierre Jolivet   if (PetscLikely(ptr)) {
619*27d4e10aSPierre Jolivet     size_type size{};
620*27d4e10aSPierre Jolivet 
621*27d4e10aSPierre Jolivet     PetscCallAbort(PETSC_COMM_SELF, pool().get_attributes(ptr, &size, nullptr));
622*27d4e10aSPierre Jolivet     PetscCallAbort(PETSC_COMM_SELF, pool().deallocate(&ptr, size, static_cast<align_type>(align)));
623*27d4e10aSPierre Jolivet   }
624*27d4e10aSPierre Jolivet   PetscFunctionReturnVoid();
625*27d4e10aSPierre Jolivet }
626*27d4e10aSPierre Jolivet #endif
627*27d4e10aSPierre Jolivet 
628*27d4e10aSPierre Jolivet // ==========================================================================================
629*27d4e10aSPierre Jolivet // PoolAllocated -- Protected API
630*27d4e10aSPierre Jolivet // ==========================================================================================
631*27d4e10aSPierre Jolivet 
pool()632*27d4e10aSPierre Jolivet PoolAllocated::allocator_type &PoolAllocated::pool() noexcept
633*27d4e10aSPierre Jolivet {
634*27d4e10aSPierre Jolivet   return pool_;
635*27d4e10aSPierre Jolivet }
636*27d4e10aSPierre Jolivet 
637*27d4e10aSPierre Jolivet } // namespace memory
638*27d4e10aSPierre Jolivet 
639*27d4e10aSPierre Jolivet } // namespace Petsc
640