xref: /petsc/include/petsc/private/cpp/register_finalize.hpp (revision 4e8208cbcbc709572b8abe32f33c78b69c819375)
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>
PetscCxxObjectRegisterFinalize(T * obj,MPI_Comm comm=PETSC_COMM_SELF)9 inline PetscErrorCode PetscCxxObjectRegisterFinalize(T *obj, MPI_Comm comm = PETSC_COMM_SELF) noexcept
10 {
11   PetscContainer contain   = nullptr;
12   const auto     finalizer = [](PetscCtxRt ctx) {
13     PetscFunctionBegin;
14     PetscCall(static_cast<T *>(*(void **)ctx)->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(PetscContainerSetCtxDestroy(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>
do_finalize_(Self && self,Args &&...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>
do_register_finalize_(Self && self,Args &&...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>
finalize_(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>
register_finalize_(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>
registered() const127 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>
finalize(Args &&...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>
finalize(Args &&...args) const160 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>
register_finalize(Args &&...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>
register_finalize(Args &&...args) const197 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