Date: 2022-03-31T04:46:08+00:00

Actual source code: objpool.hpp

      1: #ifndef PETSCOBJECTPOOL_HPP
      2: #define PETSCOBJECTPOOL_HPP

      4: #include <petscsys.h>

      6: #if defined(__cplusplus)

      8: #include <stack>
      9: #include <type_traits>

     11: namespace Petsc
     12: {

     14: // Allocator ABC for interoperability with C ctors and dtors.
     15: template <typename T>
     16: class AllocatorBase
     17: {
     18: public:
     19:   using value_type = T;

     21:   PETSC_NODISCARD PetscErrorCode create(value_type*)  noexcept;
     22:   PETSC_NODISCARD PetscErrorCode destroy(value_type&) noexcept;
     23:   PETSC_NODISCARD PetscErrorCode reset(value_type&)   noexcept;
     24:   PETSC_NODISCARD PetscErrorCode finalize()           noexcept;

     26: protected:
     27:   // make the constructor protected, this forces this class to be derived from to ever be
     28:   // instantiated
     29:   AllocatorBase() noexcept = default;
     30: };

     32: // Default allocator that performs the bare minimum of petsc object creation and
     33: // desctruction
     34: template <typename T>
     35: class CAllocator : public AllocatorBase<T>
     36: {
     37: public:
     38:   using allocator_type = AllocatorBase<T>;
     39:   using value_type     = typename allocator_type::value_type;

     41:   PETSC_NODISCARD PetscErrorCode create(value_type *obj) const noexcept
     42:   {
     43:     PetscNew(obj);
     44:     return 0;
     45:   }

     47:   PETSC_NODISCARD PetscErrorCode destroy(value_type &obj) const noexcept
     48:   {
     49:     (*obj->ops->destroy)(obj);
     50:     PetscHeaderDestroy(&obj);
     51:     return 0;
     52:   }

     54:   PETSC_NODISCARD PetscErrorCode reset(value_type &obj) const noexcept
     55:   {
     56:     this->destroy(obj);
     57:     this->create(&obj);
     58:     return 0;
     59:   }

     61:   PETSC_NODISCARD PetscErrorCode finalize() const noexcept { return 0; }
     62: };

     64: namespace detail
     65: {

     67: // Base class to object pool, defines helpful typedefs and stores the allocator instance
     68: template <typename T, class Allocator>
     69: class ObjectPoolBase
     70: {
     71: public:
     72:   using allocator_type = Allocator;
     73:   using value_type     = typename allocator_type::value_type;

     75: protected:
     76:   allocator_type alloc_;

     78:   PETSC_NODISCARD       allocator_type&  allocator()       noexcept { return alloc_; }
     79:   PETSC_NODISCARD const allocator_type& callocator() const noexcept { return alloc_; }

     81:   // default constructor
     82:   constexpr ObjectPoolBase() noexcept(std::is_nothrow_default_constructible<allocator_type>::value)
     83:     : alloc_()
     84:   { }

     86:   // const copy constructor
     87:   explicit ObjectPoolBase(const allocator_type &alloc) : alloc_(alloc) { }

     89:   // move constructor
     90:   explicit ObjectPoolBase(allocator_type &&alloc)
     91:     noexcept(std::is_nothrow_move_assignable<allocator_type>::value)
     92:     : alloc_(std::move(alloc))
     93:   { }

     95:   static_assert(std::is_base_of<AllocatorBase<value_type>,Allocator>::value,"");
     96: };

     98: } // namespace detail

    100: // default implementation, use the petsc c allocator
    101: template <typename T, class Allocator = CAllocator<T>> class ObjectPool;

    103: // multi-purpose basic object-pool, useful for recirculating old "destroyed" objects. Uses
    104: // a stack to take advantage of LIFO for memory locallity. Registers all objects to be
    105: // cleaned up on PetscFinalize()
    106: template <typename T, class Allocator>
    107: class ObjectPool : detail::ObjectPoolBase<T,Allocator>
    108: {
    109: protected:
    110:   using base_type = detail::ObjectPoolBase<T,Allocator>;

    112: public:
    113:   using allocator_type = typename base_type::allocator_type;
    114:   using value_type     = typename base_type::value_type;
    115:   using stack_type     = std::stack<value_type>;
    116:   using base_type::allocator;
    117:   using base_type::callocator;

    119: private:
    120:   stack_type stack_;
    121:   bool       registered_ = false;

    123:   PETSC_NODISCARD        PetscErrorCode registerFinalize_()     noexcept;
    124:   PETSC_NODISCARD        PetscErrorCode finalizer_()            noexcept;
    125:   PETSC_NODISCARD static PetscErrorCode staticFinalizer_(void*) noexcept;

    127: public:
    128:   // default constructor
    129:   constexpr ObjectPool() noexcept(std::is_nothrow_default_constructible<allocator_type>::value)
    130:     : stack_()
    131:   { }

    133:   // destructor
    134:   ~ObjectPool() noexcept
    135:   {
    136:     PETSC_COMM_SELF,finalizer_();
    137:   }

    139:   // copy constructor
    140:   ObjectPool(ObjectPool &other) noexcept(std::is_nothrow_copy_constructible<stack_type>::value)
    141:     : stack_(other.stack_),registered_(other.registered_)
    142:   { }

    144:   // const copy constructor
    145:   ObjectPool(const ObjectPool &other)
    146:     noexcept(std::is_nothrow_copy_constructible<stack_type>::value)
    147:     : stack_(other.stack_),registered_(other.registered_)
    148:   { }

    150:   // move constructor
    151:   ObjectPool(ObjectPool &&other) noexcept(std::is_nothrow_move_constructible<stack_type>::value)
    152:     : stack_(std::move(other.stack_)),registered_(std::move(other.registered_))
    153:   { }

    155:   // copy constructor with allocator
    156:   explicit ObjectPool(const allocator_type &alloc) : base_type(alloc) { }

    158:   // move constructor with allocator
    159:   explicit ObjectPool(allocator_type &&alloc)
    160:     noexcept(std::is_nothrow_move_constructible<allocator_type>::value)
    161:     : base_type(std::move(alloc))
    162:   { }

    164:   // Retrieve an object from the pool, if the pool is empty a new object is created instead
    165:   PETSC_NODISCARD PetscErrorCode get(value_type&)      noexcept;

    167:   // Return an object to the pool, the object need not necessarily have been created by
    168:   // the pool, note this only accepts r-value references. The pool takes ownership of all
    169:   // managed objects.
    170:   PETSC_NODISCARD PetscErrorCode reclaim(value_type&&) noexcept;

    172:   // operators
    173:   template <typename T_, class A_>
    174:   PetscBool friend operator==(const ObjectPool<T_,A_>&,const ObjectPool<T_,A_>&) noexcept;

    176:   template <typename T_, class A_>
    177:   PetscBool friend operator< (const ObjectPool<T_,A_>&,const ObjectPool<T_,A_>&) noexcept;
    178: };

    180: template <typename T, class Allocator>
    181: inline PetscBool operator==(const ObjectPool<T,Allocator> &l,const ObjectPool<T,Allocator> &r) noexcept
    182: {
    183:   return static_cast<PetscBool>(l.stack_ == r.stack_);
    184: }

    186: template <typename T, class Allocator>
    187: inline PetscBool operator< (const ObjectPool<T,Allocator> &l, const ObjectPool<T,Allocator> &r) noexcept
    188: {
    189:   return static_cast<PetscBool>(l.stack_ < r.stack_);
    190: }

    192: template <typename T, class Allocator>
    193: inline PetscBool operator!=(const ObjectPool<T,Allocator> &l, const ObjectPool<T,Allocator> &r) noexcept
    194: {
    195:   return !(l.stack_ == r.stack_);
    196: }

    198: template <typename T, class Allocator>
    199: inline PetscBool operator> (const ObjectPool<T,Allocator> &l, const ObjectPool<T,Allocator> &r) noexcept
    200: {
    201:   return r.stack_ < l.stack_;
    202: }

    204: template <typename T, class Allocator>
    205: inline PetscBool operator>=(const ObjectPool<T,Allocator> &l, const ObjectPool<T,Allocator> &r) noexcept
    206: {
    207:   return !(l.stack_ < r.stack_);
    208: }

    210: template <typename T, class Allocator>
    211: inline PetscBool operator<=(const ObjectPool<T,Allocator> &l, const ObjectPool<T,Allocator> &r) noexcept
    212: {
    213:   return !(r.stack_ < l.stack_);
    214: }

    216: template <typename T, class Allocator>
    217: inline PetscErrorCode ObjectPool<T,Allocator>::finalizer_() noexcept
    218: {
    219:   while (!stack_.empty()) {
    220:     // we do CHKERRQ __after__ the CHKERCXX on the off chance that someone uses the CXX
    221:     // error handler, we don't want to catch our own exception!
    222:     PetscCall(this->allocator().destroy(stack_.top()));
    223:     stack_.pop();
    224:   }
    225:   this->allocator().finalize();
    226:   registered_ = false;
    227:   return 0;
    228: }

    230: template <typename T, class Allocator>
    231: inline PetscErrorCode ObjectPool<T,Allocator>::staticFinalizer_(void *obj) noexcept
    232: {
    233:   static_cast<ObjectPool<T,Allocator>*>(obj)->finalizer_();
    234:   return 0;
    235: }

    237: template <typename T, class Allocator>
    238: inline PetscErrorCode ObjectPool<T,Allocator>::registerFinalize_() noexcept
    239: {
    240:   PetscContainer contain;

    242:   if (PetscLikely(registered_)) return 0;
    243:   /* use a PetscContainer as a form of thunk, it holds not only a pointer to this but
    244:      also the pointer to the static member function, which just converts the thunk back
    245:      to this. none of this would be needed if PetscRegisterFinalize() just took a void*
    246:      itself though...  */
    247:   PetscContainerCreate(PETSC_COMM_SELF,&contain);
    248:   PetscContainerSetPointer(contain,this);
    249:   PetscContainerSetUserDestroy(contain,staticFinalizer_);
    250:   PetscObjectRegisterDestroy(reinterpret_cast<PetscObject>(contain));
    251:   registered_ = true;
    252:   return 0;
    253: }

    255: template <typename T, class Allocator>
    256: inline PetscErrorCode ObjectPool<T,Allocator>::get(value_type &obj) noexcept
    257: {
    258:   registerFinalize_();
    259:   if (stack_.empty()) {
    260:     this->allocator().create(&obj);
    261:   } else {
    262:     obj = std::move(stack_.top());
    263:     stack_.pop();
    264:   }
    265:   return 0;
    266: }

    268: template <typename T, class Allocator>
    269: inline PetscErrorCode ObjectPool<T,Allocator>::reclaim(value_type &&obj) noexcept
    270: {
    271:   if (PetscLikely(registered_)) {
    272:     // allows const allocator_t& to be used if allocator defines a const reset
    273:     this->allocator().reset(obj);
    274:     stack_.push(std::move(obj));
    275:   } else {
    276:     // this is necessary if an object is "reclaimed" within another PetscFinalize() registered
    277:     // cleanup after this object pool has returned from it's finalizer. In this case, instead
    278:     // of pushing onto the stack we just destroy the object directly
    279:     this->allocator().destroy(std::move(obj));
    280:   }
    281:   return 0;
    282: }

    284: } // namespace Petsc

    286: #endif /* __cplusplus */

    288: #endif /* PETSCOBJECTPOOL_HPP */