xref: /openbmc/phosphor-dbus-monitor/src/callback.hpp (revision 223c40931e4f413431ab2f84d92c5f404e2d9cf2)
1 #pragma once
2 
3 #include "data_types.hpp"
4 
5 #include <chrono>
6 #include <sdeventplus/clock.hpp>
7 #include <sdeventplus/event.hpp>
8 #include <sdeventplus/utility/timer.hpp>
9 
10 namespace phosphor
11 {
12 namespace dbus
13 {
14 namespace monitoring
15 {
16 
17 /** @class Callback
18  *  @brief Callback interface.
19  *
20  *  Callbacks of any type can be run.
21  */
22 class Callback
23 {
24   public:
25     Callback() = default;
26     Callback(const Callback&) = delete;
27     Callback(Callback&&) = default;
28     Callback& operator=(const Callback&) = delete;
29     Callback& operator=(Callback&&) = default;
30     virtual ~Callback() = default;
31 
32     /** @brief Run the callback.
33      *  @param[in] ctx - caller context
34      *     Context could be Startup or Signal
35      *     Startup: Callback is called as part of process startup.
36      *     Signal: Callback is called as part of watch condition has been met.
37      *
38      */
39     virtual void operator()(Context ctx) = 0;
40 
41     /** @brief Run the callback.
42      *  @param[in] ctx - caller context
43      *     Context could be Startup or Signal
44      *     Startup: Callback is called as part of process startup.
45      *     Signal: Callback is called as part of watch condition has been met.
46      *  @param[in] msg - The sdbusplus signal message
47      */
48     virtual void operator()(Context ctx, sdbusplus::message::message& msg){};
49 };
50 
51 /** @class Conditional
52  *  @brief Condition interface.
53  *
54  *  Conditions of any type can be tested for true or false.
55  */
56 class Conditional
57 {
58   public:
59     Conditional() = default;
60     Conditional(const Conditional&) = delete;
61     Conditional(Conditional&&) = default;
62     Conditional& operator=(const Conditional&) = delete;
63     Conditional& operator=(Conditional&&) = default;
64     virtual ~Conditional() = default;
65 
66     /** @brief Test the condition. */
67     virtual bool operator()() = 0;
68 };
69 
70 /** @class IndexedConditional
71  *  @brief Condition with an index.
72  */
73 class IndexedConditional : public Conditional
74 {
75   public:
76     IndexedConditional() = delete;
77     IndexedConditional(const IndexedConditional&) = delete;
78     IndexedConditional(IndexedConditional&&) = default;
79     IndexedConditional& operator=(const IndexedConditional&) = delete;
80     IndexedConditional& operator=(IndexedConditional&&) = default;
81     virtual ~IndexedConditional() = default;
82 
83     explicit IndexedConditional(const PropertyIndex& conditionIndex) :
84         Conditional(), index(conditionIndex)
85     {
86     }
87 
88     /** @brief Test the condition. */
89     virtual bool operator()() override = 0;
90 
91   protected:
92     /** @brief Property names and their associated storage. */
93     const PropertyIndex& index;
94 };
95 
96 /** @class IndexedCallback
97  *  @brief Callback with an index.
98  */
99 class IndexedCallback : public Callback
100 {
101   public:
102     IndexedCallback() = delete;
103     IndexedCallback(const IndexedCallback&) = delete;
104     IndexedCallback(IndexedCallback&&) = default;
105     IndexedCallback& operator=(const IndexedCallback&) = delete;
106     IndexedCallback& operator=(IndexedCallback&&) = default;
107     virtual ~IndexedCallback() = default;
108     explicit IndexedCallback(const PropertyIndex& callbackIndex) :
109         Callback(), index(callbackIndex)
110     {
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 
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 
180     /** @brief Run the callback if the condition is satisfied. */
181     virtual void operator()(Context ctx) override
182     {
183         if (condition())
184         {
185             (*CallbackAccess::get()[graph[0]])(ctx);
186         }
187     }
188 
189   protected:
190     /** @brief The index of the callback to conditionally invoke. */
191     const std::vector<size_t>& graph;
192 
193     /** @brief The condition to test. */
194     Conditional& condition;
195 };
196 
197 /** @class DeferrableCallback
198  *
199  *  Deferrable callbacks wait a configurable period before
200  *  invoking their associated callback.
201  *
202  *  When the callback condition is initially met, start a timer.  If the
203  *  condition is tested again before the timer expires and it is not
204  *  met cancel the timer.  If the timer expires invoke the associated
205  *  callback.
206  *
207  *  @tparam CallbackAccess - Provide access to callback group instances.
208  */
209 template <typename CallbackAccess>
210 class DeferrableCallback : public ConditionalCallback<CallbackAccess>
211 {
212   public:
213     using TimerType =
214         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
215 
216     DeferrableCallback() = delete;
217     DeferrableCallback(const DeferrableCallback&) = delete;
218     DeferrableCallback(DeferrableCallback&&) = delete;
219     DeferrableCallback& operator=(const DeferrableCallback&) = delete;
220     DeferrableCallback& operator=(DeferrableCallback&&) = delete;
221     ~DeferrableCallback() = default;
222 
223     DeferrableCallback(const std::vector<size_t>& graphEntry, Conditional& cond,
224                        const std::chrono::microseconds& delay) :
225         ConditionalCallback<CallbackAccess>(graphEntry, cond),
226         delayInterval(delay), timer(nullptr)
227     {
228     }
229 
230     void operator()(Context ctx) override
231     {
232         if (!timer)
233         {
234             timer = std::make_unique<TimerType>(
235                 sdeventplus::Event::get_default(),
236                 // **INDENT-OFF**
237                 [ctx, this](auto& source) {
238                     this->ConditionalCallback<CallbackAccess>::operator()(ctx);
239                 });
240             // **INDENT-ON**
241         }
242 
243         if (this->condition())
244         {
245             if (!timer->isEnabled())
246             {
247                 // This is the first time the condition evaluated.
248                 // Start the countdown.
249                 timer->restartOnce(delayInterval);
250             }
251         }
252         else
253         {
254             // The condition did not evaluate.  Stop the countdown.
255             timer->setEnabled(false);
256         }
257     }
258 
259   private:
260     /** @brief The length to wait for the condition to stop evaluating. */
261     std::chrono::microseconds delayInterval;
262 
263     /** @brief Delegated timer functions. */
264     std::unique_ptr<TimerType> timer;
265 };
266 
267 } // namespace monitoring
268 } // namespace dbus
269 } // namespace phosphor
270