xref: /petsc/include/petsc/private/cpp/register_finalize.hpp (revision 609729f98f8a298ca9224802b9f174c43070f4ac)
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   PetscValidPointer(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