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