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