xref: /openbmc/phosphor-dbus-monitor/src/callback.hpp (revision 3fe976cc22e579860f5b1832d920636d93145507)
1 #pragma once
2 
3 #include "data_types.hpp"
4 
5 #include <sdeventplus/clock.hpp>
6 #include <sdeventplus/event.hpp>
7 #include <sdeventplus/utility/timer.hpp>
8 
9 #include <chrono>
10 #include <cstddef>
11 
12 namespace phosphor
13 {
14 namespace dbus
15 {
16 namespace monitoring
17 {
18 
19 /** @class Callback
20  *  @brief Callback interface.
21  *
22  *  Callbacks of any type can be run.
23  */
24 class Callback
25 {
26   public:
27     Callback() = default;
28     Callback(const Callback&) = delete;
29     Callback(Callback&&) = default;
30     Callback& operator=(const Callback&) = delete;
31     Callback& operator=(Callback&&) = default;
32     virtual ~Callback() = default;
33 
34     /** @brief Run the callback.
35      *  @param[in] ctx - caller context
36      *     Context could be Startup or Signal
37      *     Startup: Callback is called as part of process startup.
38      *     Signal: Callback is called as part of watch condition has been met.
39      *
40      */
41     virtual void operator()(Context ctx) = 0;
42 
43     /** @brief Run the callback.
44      *  @param[in] ctx - caller context
45      *     Context could be Startup or Signal
46      *     Startup: Callback is called as part of process startup.
47      *     Signal: Callback is called as part of watch condition has been met.
48      *  @param[in] msg - The sdbusplus signal message
49      */
50     virtual void operator()(Context ctx, sdbusplus::message::message& msg){};
51 };
52 
53 /** @class Conditional
54  *  @brief Condition interface.
55  *
56  *  Conditions of any type can be tested for true or false.
57  */
58 class Conditional
59 {
60   public:
61     Conditional() = default;
62     Conditional(const Conditional&) = delete;
63     Conditional(Conditional&&) = default;
64     Conditional& operator=(const Conditional&) = delete;
65     Conditional& operator=(Conditional&&) = default;
66     virtual ~Conditional() = default;
67 
68     /** @brief Test the condition. */
69     virtual bool operator()() = 0;
70 };
71 
72 /** @class IndexedConditional
73  *  @brief Condition with an index.
74  */
75 class IndexedConditional : public Conditional
76 {
77   public:
78     IndexedConditional() = delete;
79     IndexedConditional(const IndexedConditional&) = delete;
80     IndexedConditional(IndexedConditional&&) = default;
81     IndexedConditional& operator=(const IndexedConditional&) = delete;
82     IndexedConditional& operator=(IndexedConditional&&) = default;
83     virtual ~IndexedConditional() = default;
84 
85     explicit IndexedConditional(const PropertyIndex& conditionIndex) :
86         Conditional(), index(conditionIndex)
87     {}
88 
89     /** @brief Test the condition. */
90     virtual bool operator()() override = 0;
91 
92   protected:
93     /** @brief Property names and their associated storage. */
94     const PropertyIndex& index;
95 };
96 
97 /** @class IndexedCallback
98  *  @brief Callback with an index.
99  */
100 class IndexedCallback : public Callback
101 {
102   public:
103     IndexedCallback() = delete;
104     IndexedCallback(const IndexedCallback&) = delete;
105     IndexedCallback(IndexedCallback&&) = default;
106     IndexedCallback& operator=(const IndexedCallback&) = delete;
107     IndexedCallback& operator=(IndexedCallback&&) = default;
108     virtual ~IndexedCallback() = default;
109     explicit IndexedCallback(const PropertyIndex& callbackIndex) :
110         Callback(), index(callbackIndex)
111     {}
112 
113     /** @brief Run the callback. */
114     virtual void operator()(Context ctx) override = 0;
115 
116   protected:
117     /** @brief Property names and their associated storage. */
118     const PropertyIndex& index;
119 };
120 
121 /** @class GroupOfCallbacks
122  *  @brief Invoke multiple callbacks.
123  *
124  *  A group of callbacks is implemented as a vector of array indices
125  *  into an external array  of callbacks.  The group function call
126  *  operator traverses the vector of indices, invoking each
127  *  callback.
128  *
129  *  @tparam CallbackAccess - Access to the array of callbacks.
130  */
131 template <typename CallbackAccess>
132 class GroupOfCallbacks : public Callback
133 {
134   public:
135     GroupOfCallbacks() = delete;
136     GroupOfCallbacks(const GroupOfCallbacks&) = delete;
137     GroupOfCallbacks(GroupOfCallbacks&&) = default;
138     GroupOfCallbacks& operator=(const GroupOfCallbacks&) = delete;
139     GroupOfCallbacks& operator=(GroupOfCallbacks&&) = default;
140     ~GroupOfCallbacks() = default;
141     explicit GroupOfCallbacks(const std::vector<size_t>& graphEntry) :
142         graph(graphEntry)
143     {}
144 
145     /** @brief Run the callbacks. */
146     void operator()(Context ctx) override
147     {
148         for (auto e : graph)
149         {
150             (*CallbackAccess::get()[e])(ctx);
151         }
152     }
153 
154   private:
155     /** @brief The offsets of the callbacks in the group. */
156     const std::vector<size_t>& graph;
157 };
158 
159 /** @class ConditionalCallback
160  *  @brief Callback adaptor that asssociates a condition with a callback.
161  */
162 template <typename CallbackAccess>
163 class ConditionalCallback : public Callback
164 {
165   public:
166     ConditionalCallback() = delete;
167     ConditionalCallback(const ConditionalCallback&) = delete;
168     ConditionalCallback(ConditionalCallback&&) = default;
169     ConditionalCallback& operator=(const ConditionalCallback&) = delete;
170     ConditionalCallback& operator=(ConditionalCallback&&) = default;
171     virtual ~ConditionalCallback() = default;
172     ConditionalCallback(const std::vector<size_t>& graphEntry,
173                         Conditional& cond) :
174         graph(graphEntry),
175         condition(cond)
176     {}
177 
178     /** @brief Run the callback if the condition is satisfied. */
179     virtual void operator()(Context ctx) override
180     {
181         if (condition())
182         {
183             (*CallbackAccess::get()[graph[0]])(ctx);
184         }
185     }
186 
187   protected:
188     /** @brief The index of the callback to conditionally invoke. */
189     const std::vector<size_t>& graph;
190 
191     /** @brief The condition to test. */
192     Conditional& condition;
193 };
194 
195 /** @class DeferrableCallback
196  *
197  *  Deferrable callbacks wait a configurable period before
198  *  invoking their associated callback.
199  *
200  *  When the callback condition is initially met, start a timer.  If the
201  *  condition is tested again before the timer expires and it is not
202  *  met cancel the timer.  If the timer expires invoke the associated
203  *  callback.
204  *
205  *  @tparam CallbackAccess - Provide access to callback group instances.
206  */
207 template <typename CallbackAccess>
208 class DeferrableCallback : public ConditionalCallback<CallbackAccess>
209 {
210   public:
211     using TimerType =
212         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
213 
214     DeferrableCallback() = delete;
215     DeferrableCallback(const DeferrableCallback&) = delete;
216     DeferrableCallback(DeferrableCallback&&) = delete;
217     DeferrableCallback& operator=(const DeferrableCallback&) = delete;
218     DeferrableCallback& operator=(DeferrableCallback&&) = delete;
219     ~DeferrableCallback() = default;
220 
221     DeferrableCallback(const std::vector<size_t>& graphEntry, Conditional& cond,
222                        const std::chrono::microseconds& delay) :
223         ConditionalCallback<CallbackAccess>(graphEntry, cond),
224         delayInterval(delay), timer(nullptr)
225     {}
226 
227     /** @brief Start internal timer if the condition is satisfied.
228      *
229      * When the timer expires, it calls operator() for the
230      * ConditionalCallback with the context saved in
231      * DeferrableCallback instance.
232      */
233     void operator()(Context ctx) override
234     {
235         if (!timer)
236         {
237             timer = std::make_unique<TimerType>(
238                 sdeventplus::Event::get_default(),
239                 // **INDENT-OFF**
240                 [this](auto& source) {
241                     // The timer uses the context saved on timer enable
242                     this->ConditionalCallback<CallbackAccess>::operator()(
243                         this->ctx);
244                 });
245             // **INDENT-ON**
246         }
247 
248         if (this->condition())
249         {
250             if (!timer->isEnabled())
251             {
252                 // This is the first time the condition evaluated.
253                 // Save current context for timer use.
254                 this->ctx = ctx;
255                 // Start the countdown.
256                 timer->restartOnce(delayInterval);
257             }
258         }
259         else
260         {
261             // The condition did not evaluate.  Stop the countdown.
262             timer->setEnabled(false);
263         }
264     }
265 
266   private:
267     /** @brief The length to wait for the condition to stop evaluating. */
268     std::chrono::microseconds delayInterval;
269 
270     /** @brief Delegated timer functions. */
271     std::unique_ptr<TimerType> timer;
272 
273     /** @brief Current context for timer. */
274     Context ctx;
275 };
276 
277 } // namespace monitoring
278 } // namespace dbus
279 } // namespace phosphor
280