xref: /openbmc/phosphor-dbus-monitor/src/callback.hpp (revision c0fd1f8a08ea141158b717fcc8960a59da297daf)
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 */,
51                             sdbusplus::message::message& /* msg */){};
52 };
53 
54 /** @class Conditional
55  *  @brief Condition interface.
56  *
57  *  Conditions of any type can be tested for true or false.
58  */
59 class Conditional
60 {
61   public:
62     Conditional() = default;
63     Conditional(const Conditional&) = delete;
64     Conditional(Conditional&&) = default;
65     Conditional& operator=(const Conditional&) = delete;
66     Conditional& operator=(Conditional&&) = default;
67     virtual ~Conditional() = default;
68 
69     /** @brief Test the condition. */
70     virtual bool operator()() = 0;
71 };
72 
73 /** @class IndexedConditional
74  *  @brief Condition with an index.
75  */
76 class IndexedConditional : public Conditional
77 {
78   public:
79     IndexedConditional() = delete;
80     IndexedConditional(const IndexedConditional&) = delete;
81     IndexedConditional(IndexedConditional&&) = default;
82     IndexedConditional& operator=(const IndexedConditional&) = delete;
83     IndexedConditional& operator=(IndexedConditional&&) = default;
84     virtual ~IndexedConditional() = default;
85 
86     explicit IndexedConditional(const PropertyIndex& conditionIndex) :
87         Conditional(), index(conditionIndex)
88     {}
89 
90     /** @brief Test the condition. */
91     virtual bool operator()() override = 0;
92 
93   protected:
94     /** @brief Property names and their associated storage. */
95     const PropertyIndex& index;
96 };
97 
98 /** @class IndexedCallback
99  *  @brief Callback with an index.
100  */
101 class IndexedCallback : public Callback
102 {
103   public:
104     IndexedCallback() = delete;
105     IndexedCallback(const IndexedCallback&) = delete;
106     IndexedCallback(IndexedCallback&&) = default;
107     IndexedCallback& operator=(const IndexedCallback&) = delete;
108     IndexedCallback& operator=(IndexedCallback&&) = default;
109     virtual ~IndexedCallback() = default;
110     explicit IndexedCallback(const PropertyIndex& callbackIndex) :
111         Callback(), index(callbackIndex)
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     /** @brief Run the callbacks. */
147     void operator()(Context ctx) override
148     {
149         for (auto e : graph)
150         {
151             (*CallbackAccess::get()[e])(ctx);
152         }
153     }
154 
155   private:
156     /** @brief The offsets of the callbacks in the group. */
157     const std::vector<size_t>& graph;
158 };
159 
160 /** @class ConditionalCallback
161  *  @brief Callback adaptor that asssociates a condition with a callback.
162  */
163 template <typename CallbackAccess>
164 class ConditionalCallback : public Callback
165 {
166   public:
167     ConditionalCallback() = delete;
168     ConditionalCallback(const ConditionalCallback&) = delete;
169     ConditionalCallback(ConditionalCallback&&) = default;
170     ConditionalCallback& operator=(const ConditionalCallback&) = delete;
171     ConditionalCallback& operator=(ConditionalCallback&&) = default;
172     virtual ~ConditionalCallback() = default;
173     ConditionalCallback(const std::vector<size_t>& graphEntry,
174                         Conditional& cond) :
175         graph(graphEntry),
176         condition(cond)
177     {}
178 
179     /** @brief Run the callback if the condition is satisfied. */
180     virtual void operator()(Context ctx) override
181     {
182         if (condition())
183         {
184             (*CallbackAccess::get()[graph[0]])(ctx);
185         }
186     }
187 
188   protected:
189     /** @brief The index of the callback to conditionally invoke. */
190     const std::vector<size_t>& graph;
191 
192     /** @brief The condition to test. */
193     Conditional& condition;
194 };
195 
196 /** @class DeferrableCallback
197  *
198  *  Deferrable callbacks wait a configurable period before
199  *  invoking their associated callback.
200  *
201  *  When the callback condition is initially met, start a timer.  If the
202  *  condition is tested again before the timer expires and it is not
203  *  met cancel the timer.  If the timer expires invoke the associated
204  *  callback.
205  *
206  *  @tparam CallbackAccess - Provide access to callback group instances.
207  */
208 template <typename CallbackAccess>
209 class DeferrableCallback : public ConditionalCallback<CallbackAccess>
210 {
211   public:
212     using TimerType =
213         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
214 
215     DeferrableCallback() = delete;
216     DeferrableCallback(const DeferrableCallback&) = delete;
217     DeferrableCallback(DeferrableCallback&&) = delete;
218     DeferrableCallback& operator=(const DeferrableCallback&) = delete;
219     DeferrableCallback& operator=(DeferrableCallback&&) = delete;
220     ~DeferrableCallback() = default;
221 
222     DeferrableCallback(const std::vector<size_t>& graphEntry, Conditional& cond,
223                        const std::chrono::microseconds& delay) :
224         ConditionalCallback<CallbackAccess>(graphEntry, cond),
225         delayInterval(delay), timer(nullptr)
226     {}
227 
228     /** @brief Start internal timer if the condition is satisfied.
229      *
230      * When the timer expires, it calls operator() for the
231      * ConditionalCallback with the context saved in
232      * DeferrableCallback instance.
233      */
234     void operator()(Context ctx) override
235     {
236         if (!timer)
237         {
238             timer = std::make_unique<TimerType>(
239                 sdeventplus::Event::get_default(),
240                 // **INDENT-OFF**
241                 [this](auto& /* source */) {
242                     // The timer uses the context saved on timer enable
243                     this->ConditionalCallback<CallbackAccess>::operator()(
244                         this->ctx);
245                 });
246             // **INDENT-ON**
247         }
248 
249         if (this->condition())
250         {
251             if (!timer->isEnabled())
252             {
253                 // This is the first time the condition evaluated.
254                 // Save current context for timer use.
255                 this->ctx = ctx;
256                 // Start the countdown.
257                 timer->restartOnce(delayInterval);
258             }
259         }
260         else
261         {
262             // The condition did not evaluate.  Stop the countdown.
263             timer->setEnabled(false);
264         }
265     }
266 
267   private:
268     /** @brief The length to wait for the condition to stop evaluating. */
269     std::chrono::microseconds delayInterval;
270 
271     /** @brief Delegated timer functions. */
272     std::unique_ptr<TimerType> timer;
273 
274     /** @brief Current context for timer. */
275     Context ctx;
276 };
277 
278 } // namespace monitoring
279 } // namespace dbus
280 } // namespace phosphor
281