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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 154*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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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