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