1 #ifndef PETSC_CPP_REGISTER_FINALIZE_HPP 2 #define PETSC_CPP_REGISTER_FINALIZE_HPP 3 4 #include <petscsys.h> 5 6 #include <petsc/private/cpp/crtp.hpp> 7 #include <petsc/private/cpp/type_traits.hpp> 8 9 template <typename T> 10 inline PetscErrorCode PetscCxxObjectRegisterFinalize(T *obj, MPI_Comm comm = PETSC_COMM_SELF) noexcept 11 { 12 PetscContainer contain = nullptr; 13 const auto finalizer = [](void *ptr) { 14 PetscFunctionBegin; 15 PetscCall(static_cast<T *>(ptr)->finalize()); 16 PetscFunctionReturn(PETSC_SUCCESS); 17 }; 18 19 PetscFunctionBegin; 20 PetscAssertPointer(obj, 1); 21 PetscCall(PetscContainerCreate(comm, &contain)); 22 PetscCall(PetscContainerSetPointer(contain, obj)); 23 PetscCall(PetscContainerSetUserDestroy(contain, std::move(finalizer))); 24 PetscCall(PetscObjectRegisterDestroy(reinterpret_cast<PetscObject>(contain))); 25 PetscFunctionReturn(PETSC_SUCCESS); 26 } 27 28 namespace Petsc 29 { 30 31 // ========================================================================================== 32 // RegisterFinalizeable 33 // 34 // A mixin class that enables registering a finalizer for a class instance to run during 35 // PetscFinalize(). Enables 3 public methods: 36 // 37 // 1. register_finalize() - Register the calling instance to run the member function 38 // finalize_() during PetscFinalize(). It only registers the class once. 39 // 2. finalize() - Run the member function finalize_() immediately. 40 // 3. registered() - Query whether you are registered. 41 // ========================================================================================== 42 template <typename Derived> 43 class RegisterFinalizeable : public util::crtp<RegisterFinalizeable, Derived> { 44 public: 45 using derived_type = Derived; 46 47 PETSC_NODISCARD bool registered() const noexcept; 48 template <typename... Args> 49 PetscErrorCode finalize(Args &&...) noexcept; 50 template <typename... Args> 51 PetscErrorCode finalize(Args &&...) const noexcept; 52 template <typename... Args> 53 PetscErrorCode register_finalize(Args &&...) noexcept; 54 template <typename... Args> 55 PetscErrorCode register_finalize(Args &&...) const noexcept; 56 57 private: 58 constexpr RegisterFinalizeable() noexcept = default; 59 friend derived_type; 60 61 template <typename Self, typename... Args> 62 static PetscErrorCode do_finalize_(Self &&, Args &&...) noexcept; 63 template <typename Self, typename... Args> 64 static PetscErrorCode do_register_finalize_(Self &&, Args &&...) noexcept; 65 66 // default implementations if the derived class does not want to implement them 67 template <typename... Args> 68 static constexpr PetscErrorCode finalize_(Args &&...) noexcept; 69 template <typename... Args> 70 static constexpr PetscErrorCode register_finalize_(Args &&...) noexcept; 71 72 mutable bool registered_ = false; 73 }; 74 75 template <typename D> 76 template <typename Self, typename... Args> 77 inline PetscErrorCode RegisterFinalizeable<D>::do_finalize_(Self &&self, Args &&...args) noexcept 78 { 79 PetscFunctionBegin; 80 // order of setting registered_ to false matters here, if the finalizer wants to re-register 81 // it should be able to 82 if (self.underlying().registered()) { 83 self.registered_ = false; 84 PetscCall(self.underlying().finalize_(std::forward<Args>(args)...)); 85 } 86 PetscFunctionReturn(PETSC_SUCCESS); 87 } 88 89 template <typename D> 90 template <typename Self, typename... Args> 91 inline PetscErrorCode RegisterFinalizeable<D>::do_register_finalize_(Self &&self, Args &&...args) noexcept 92 { 93 PetscFunctionBegin; 94 if (PetscLikely(self.underlying().registered())) PetscFunctionReturn(PETSC_SUCCESS); 95 self.registered_ = true; 96 PetscCall(self.underlying().register_finalize_(std::forward<Args>(args)...)); 97 // Check if registered before we commit to actually register-finalizing. register_finalize_() 98 // is allowed to run its finalizer immediately 99 if (self.underlying().registered()) { 100 using decayed_type = util::decay_t<Self>; 101 102 PetscCall(PetscCxxObjectRegisterFinalize(const_cast<decayed_type *>(std::addressof(self)))); 103 } 104 PetscFunctionReturn(PETSC_SUCCESS); 105 } 106 107 template <typename D> 108 template <typename... Args> 109 inline constexpr PetscErrorCode RegisterFinalizeable<D>::finalize_(Args &&...) noexcept 110 { 111 return PETSC_SUCCESS; 112 } 113 114 template <typename D> 115 template <typename... Args> 116 inline constexpr PetscErrorCode RegisterFinalizeable<D>::register_finalize_(Args &&...) noexcept 117 { 118 return PETSC_SUCCESS; 119 } 120 121 /* 122 RegisterFinalizeable::registered - Determine if the class instance is registered 123 124 Notes: 125 Returns true if class is registered, false otherwise. 126 */ 127 template <typename D> 128 inline bool RegisterFinalizeable<D>::registered() const noexcept 129 { 130 return registered_; 131 } 132 133 /* 134 RegisterFinalizeable::finalize - Run the finalizer for a class 135 136 Input Parameters: 137 138 . ...args - A set of arguments to pass to the finalizer 139 140 Notes: 141 It is not necessary to implement finalize_() in the derived class (though pretty much 142 pointless), a default (no-op) implementation is provided. 143 144 Runs the member function finalize_() with args forwarded. 145 146 "Unregisters" the class from PetscFinalize(). However, it is safe for finalize_() to 147 re-register itself (via register_finalize()). registered() is guaranteed to return false 148 inside finalize_(). 149 */ 150 template <typename D> 151 template <typename... Args> 152 inline PetscErrorCode RegisterFinalizeable<D>::finalize(Args &&...args) noexcept 153 { 154 PetscFunctionBegin; 155 PetscCall(do_finalize_(*this, std::forward<Args>(args)...)); 156 PetscFunctionReturn(PETSC_SUCCESS); 157 } 158 159 template <typename D> 160 template <typename... Args> 161 inline PetscErrorCode RegisterFinalizeable<D>::finalize(Args &&...args) const noexcept 162 { 163 PetscFunctionBegin; 164 PetscCall(do_finalize_(*this, std::forward<Args>(args)...)); 165 PetscFunctionReturn(PETSC_SUCCESS); 166 } 167 168 /* 169 RegisterFinalizeable::register_finalize - Register a finalizer to run during PetscFinalize() 170 171 Input Parameters: 172 . ...args - Additional arguments to pass to the register_finalize_() hook 173 174 Notes: 175 It is not necessary to implement register_finalize_() in the derived class. A default (no-op) 176 implementation is provided. 177 178 Before registering the class, the register_finalize_() hook function is run. This is useful 179 for running any one-time setup code before registering. Subsequent invocations of this 180 function (as long as registered() returns true) will not run register_finalize_() again. 181 182 The class is considered registered before calling the hook, that is registered() will always 183 return true inside register_finalize_(). register_finalize_() is allowed to immediately 184 un-register the class (via finalize()). In this case the finalizer does not run at 185 PetscFinalize(), and registered() returns false after this routine returns. 186 */ 187 template <typename D> 188 template <typename... Args> 189 inline PetscErrorCode RegisterFinalizeable<D>::register_finalize(Args &&...args) noexcept 190 { 191 PetscFunctionBegin; 192 PetscCall(do_register_finalize_(*this, std::forward<Args>(args)...)); 193 PetscFunctionReturn(PETSC_SUCCESS); 194 } 195 196 template <typename D> 197 template <typename... Args> 198 inline PetscErrorCode RegisterFinalizeable<D>::register_finalize(Args &&...args) const noexcept 199 { 200 PetscFunctionBegin; 201 PetscCall(do_register_finalize_(*this, std::forward<Args>(args)...)); 202 PetscFunctionReturn(PETSC_SUCCESS); 203 } 204 205 } // namespace Petsc 206 207 #endif // PETSC_CPP_REGISTER_FINALIZE_HPP 208