1 #pragma once 2 3 #include <petscsys.h> 4 5 #include <petsc/private/cpp/crtp.hpp> 6 #include <petsc/private/cpp/type_traits.hpp> 7 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 }; 17 18 PetscFunctionBegin; 19 PetscAssertPointer(obj, 1); 20 PetscCall(PetscContainerCreate(comm, &contain)); 21 PetscCall(PetscContainerSetPointer(contain, obj)); 22 PetscCall(PetscContainerSetUserDestroy(contain, std::move(finalizer))); 23 PetscCall(PetscObjectRegisterDestroy(reinterpret_cast<PetscObject>(contain))); 24 PetscFunctionReturn(PETSC_SUCCESS); 25 } 26 27 namespace Petsc 28 { 29 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; 45 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; 55 56 private: 57 constexpr RegisterFinalizeable() noexcept = default; 58 friend derived_type; 59 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; 64 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; 70 71 mutable bool registered_ = false; 72 }; 73 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 } 87 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>; 100 101 PetscCall(PetscCxxObjectRegisterFinalize(const_cast<decayed_type *>(std::addressof(self)))); 102 } 103 PetscFunctionReturn(PETSC_SUCCESS); 104 } 105 106 template <typename D> 107 template <typename... Args> 108 inline constexpr PetscErrorCode RegisterFinalizeable<D>::finalize_(Args &&...) noexcept 109 { 110 return PETSC_SUCCESS; 111 } 112 113 template <typename D> 114 template <typename... Args> 115 inline constexpr PetscErrorCode RegisterFinalizeable<D>::register_finalize_(Args &&...) noexcept 116 { 117 return PETSC_SUCCESS; 118 } 119 120 /* 121 RegisterFinalizeable::registered - Determine if the class instance is registered 122 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 } 131 132 /* 133 RegisterFinalizeable::finalize - Run the finalizer for a class 134 135 Input Parameters: 136 137 . ...args - A set of arguments to pass to the finalizer 138 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. 142 143 Runs the member function finalize_() with args forwarded. 144 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 } 157 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 } 166 167 /* 168 RegisterFinalizeable::register_finalize - Register a finalizer to run during PetscFinalize() 169 170 Input Parameters: 171 . ...args - Additional arguments to pass to the register_finalize_() hook 172 173 Notes: 174 It is not necessary to implement register_finalize_() in the derived class. A default (no-op) 175 implementation is provided. 176 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. 180 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 } 194 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 } 203 204 } // namespace Petsc 205