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