Actual source code: register_finalize.hpp

      1: #pragma once

      3: #include <petscsys.h>

      5: #include <petsc/private/cpp/crtp.hpp>
      6: #include <petsc/private/cpp/type_traits.hpp>

      8: template <typename T>
      9: inline PetscErrorCode PetscCxxObjectRegisterFinalize(T *obj, MPI_Comm comm = PETSC_COMM_SELF) noexcept
     10: {
     11:   PetscContainer contain   = nullptr;
     12:   const auto     finalizer = [](void **ptr) {
     13:     PetscFunctionBegin;
     14:     PetscCall(static_cast<T *>(*ptr)->finalize());
     15:     PetscFunctionReturn(PETSC_SUCCESS);
     16:   };

     18:   PetscFunctionBegin;
     19:   PetscAssertPointer(obj, 1);
     20:   PetscCall(PetscContainerCreate(comm, &contain));
     21:   PetscCall(PetscContainerSetPointer(contain, obj));
     22:   PetscCall(PetscContainerSetCtxDestroy(contain, std::move(finalizer)));
     23:   PetscCall(PetscObjectRegisterDestroy(reinterpret_cast<PetscObject>(contain)));
     24:   PetscFunctionReturn(PETSC_SUCCESS);
     25: }

     27: namespace Petsc
     28: {

     30: // ==========================================================================================
     31: // RegisterFinalizeable
     32: //
     33: // A mixin class that enables registering a finalizer for a class instance to run during
     34: // PetscFinalize(). Enables 3 public methods:
     35: //
     36: // 1. register_finalize() - Register the calling instance to run the member function
     37: //    finalize_() during PetscFinalize(). It only registers the class once.
     38: // 2. finalize() - Run the member function finalize_() immediately.
     39: // 3. registered() - Query whether you are registered.
     40: // ==========================================================================================
     41: template <typename Derived>
     42: class RegisterFinalizeable : public util::crtp<RegisterFinalizeable, Derived> {
     43: public:
     44:   using derived_type = Derived;

     46:   PETSC_NODISCARD bool registered() const noexcept;
     47:   template <typename... Args>
     48:   PetscErrorCode finalize(Args &&...) noexcept;
     49:   template <typename... Args>
     50:   PetscErrorCode finalize(Args &&...) const noexcept;
     51:   template <typename... Args>
     52:   PetscErrorCode register_finalize(Args &&...) noexcept;
     53:   template <typename... Args>
     54:   PetscErrorCode register_finalize(Args &&...) const noexcept;

     56: private:
     57:   constexpr RegisterFinalizeable() noexcept = default;
     58:   friend derived_type;

     60:   template <typename Self, typename... Args>
     61:   static PetscErrorCode do_finalize_(Self &&, Args &&...) noexcept;
     62:   template <typename Self, typename... Args>
     63:   static PetscErrorCode do_register_finalize_(Self &&, Args &&...) noexcept;

     65:   // default implementations if the derived class does not want to implement them
     66:   template <typename... Args>
     67:   static constexpr PetscErrorCode finalize_(Args &&...) noexcept;
     68:   template <typename... Args>
     69:   static constexpr PetscErrorCode register_finalize_(Args &&...) noexcept;

     71:   mutable bool registered_ = false;
     72: };

     74: template <typename D>
     75: template <typename Self, typename... Args>
     76: inline PetscErrorCode RegisterFinalizeable<D>::do_finalize_(Self &&self, Args &&...args) noexcept
     77: {
     78:   PetscFunctionBegin;
     79:   // order of setting registered_ to false matters here, if the finalizer wants to re-register
     80:   // it should be able to
     81:   if (self.underlying().registered()) {
     82:     self.registered_ = false;
     83:     PetscCall(self.underlying().finalize_(std::forward<Args>(args)...));
     84:   }
     85:   PetscFunctionReturn(PETSC_SUCCESS);
     86: }

     88: template <typename D>
     89: template <typename Self, typename... Args>
     90: inline PetscErrorCode RegisterFinalizeable<D>::do_register_finalize_(Self &&self, Args &&...args) noexcept
     91: {
     92:   PetscFunctionBegin;
     93:   if (PetscLikely(self.underlying().registered())) PetscFunctionReturn(PETSC_SUCCESS);
     94:   self.registered_ = true;
     95:   PetscCall(self.underlying().register_finalize_(std::forward<Args>(args)...));
     96:   // Check if registered before we commit to actually register-finalizing. register_finalize_()
     97:   // is allowed to run its finalizer immediately
     98:   if (self.underlying().registered()) {
     99:     using decayed_type = util::decay_t<Self>;

    101:     PetscCall(PetscCxxObjectRegisterFinalize(const_cast<decayed_type *>(std::addressof(self))));
    102:   }
    103:   PetscFunctionReturn(PETSC_SUCCESS);
    104: }

    106: template <typename D>
    107: template <typename... Args>
    108: inline constexpr PetscErrorCode RegisterFinalizeable<D>::finalize_(Args &&...) noexcept
    109: {
    110:   return PETSC_SUCCESS;
    111: }

    113: template <typename D>
    114: template <typename... Args>
    115: inline constexpr PetscErrorCode RegisterFinalizeable<D>::register_finalize_(Args &&...) noexcept
    116: {
    117:   return PETSC_SUCCESS;
    118: }

    120: /*
    121:   RegisterFinalizeable::registered - Determine if the class instance is registered

    123:   Notes:
    124:   Returns true if class is registered, false otherwise.
    125: */
    126: template <typename D>
    127: inline bool RegisterFinalizeable<D>::registered() const noexcept
    128: {
    129:   return registered_;
    130: }

    132: /*
    133:   RegisterFinalizeable::finalize - Run the finalizer for a class

    135:   Input Parameters:

    137: . ...args - A set of arguments to pass to the finalizer

    139:   Notes:
    140:   It is not necessary to implement finalize_() in the derived class (though pretty much
    141:   pointless), a default (no-op) implementation is provided.

    143:   Runs the member function finalize_() with args forwarded.

    145:   "Unregisters" the class from PetscFinalize(). However, it is safe for finalize_() to
    146:   re-register itself (via register_finalize()). registered() is guaranteed to return false
    147:   inside finalize_().
    148: */
    149: template <typename D>
    150: template <typename... Args>
    151: inline PetscErrorCode RegisterFinalizeable<D>::finalize(Args &&...args) noexcept
    152: {
    153:   PetscFunctionBegin;
    154:   PetscCall(do_finalize_(*this, std::forward<Args>(args)...));
    155:   PetscFunctionReturn(PETSC_SUCCESS);
    156: }

    158: template <typename D>
    159: template <typename... Args>
    160: inline PetscErrorCode RegisterFinalizeable<D>::finalize(Args &&...args) const noexcept
    161: {
    162:   PetscFunctionBegin;
    163:   PetscCall(do_finalize_(*this, std::forward<Args>(args)...));
    164:   PetscFunctionReturn(PETSC_SUCCESS);
    165: }

    167: /*
    168:   RegisterFinalizeable::register_finalize - Register a finalizer to run during PetscFinalize()

    170:   Input Parameters:
    171: . ...args - Additional arguments to pass to the register_finalize_() hook

    173:   Notes:
    174:   It is not necessary to implement register_finalize_() in the derived class. A default (no-op)
    175:   implementation is provided.

    177:   Before registering the class, the register_finalize_() hook function is run. This is useful
    178:   for running any one-time setup code before registering. Subsequent invocations of this
    179:   function (as long as registered() returns true) will not run register_finalize_() again.

    181:   The class is considered registered before calling the hook, that is registered() will always
    182:   return true inside register_finalize_(). register_finalize_() is allowed to immediately
    183:   un-register the class (via finalize()). In this case the finalizer does not run at
    184:   PetscFinalize(), and registered() returns false after this routine returns.
    185: */
    186: template <typename D>
    187: template <typename... Args>
    188: inline PetscErrorCode RegisterFinalizeable<D>::register_finalize(Args &&...args) noexcept
    189: {
    190:   PetscFunctionBegin;
    191:   PetscCall(do_register_finalize_(*this, std::forward<Args>(args)...));
    192:   PetscFunctionReturn(PETSC_SUCCESS);
    193: }

    195: template <typename D>
    196: template <typename... Args>
    197: inline PetscErrorCode RegisterFinalizeable<D>::register_finalize(Args &&...args) const noexcept
    198: {
    199:   PetscFunctionBegin;
    200:   PetscCall(do_register_finalize_(*this, std::forward<Args>(args)...));
    201:   PetscFunctionReturn(PETSC_SUCCESS);
    202: }

    204: } // namespace Petsc