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