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