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