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      */
operator ()(Context,sdbusplus::message_t &)50     virtual void operator()(Context /* ctx */,
51                             sdbusplus::message_t& /* 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 
IndexedConditional(const PropertyIndex & conditionIndex)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;
IndexedCallback(const PropertyIndex & callbackIndex)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;
GroupOfCallbacks(const std::vector<size_t> & graphEntry)142     explicit GroupOfCallbacks(const std::vector<size_t>& graphEntry) :
143         graph(graphEntry)
144     {}
145 
146     /** @brief Run the callbacks. */
operator ()(Context ctx)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 associates 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;
ConditionalCallback(const std::vector<size_t> & graphEntry,Conditional & cond)173     ConditionalCallback(const std::vector<size_t>& graphEntry,
174                         Conditional& cond) : graph(graphEntry), condition(cond)
175     {}
176 
177     /** @brief Run the callback if the condition is satisfied. */
operator ()(Context ctx)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  */
206 template <typename CallbackAccess>
207 class DeferrableCallback : public ConditionalCallback<CallbackAccess>
208 {
209   public:
210     using TimerType =
211         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
212 
213     DeferrableCallback() = delete;
214     DeferrableCallback(const DeferrableCallback&) = delete;
215     DeferrableCallback(DeferrableCallback&&) = delete;
216     DeferrableCallback& operator=(const DeferrableCallback&) = delete;
217     DeferrableCallback& operator=(DeferrableCallback&&) = delete;
218     ~DeferrableCallback() = default;
219 
DeferrableCallback(const std::vector<size_t> & graphEntry,Conditional & cond,const std::chrono::microseconds & delay)220     DeferrableCallback(const std::vector<size_t>& graphEntry, Conditional& cond,
221                        const std::chrono::microseconds& delay) :
222         ConditionalCallback<CallbackAccess>(graphEntry, cond),
223         delayInterval(delay)
224     {}
225 
226     /** @brief Start internal timer if the condition is satisfied.
227      *
228      * When the timer expires, it calls operator() for the
229      * ConditionalCallback with the context saved in
230      * DeferrableCallback instance.
231      */
operator ()(Context ctx)232     void operator()(Context ctx) override
233     {
234         if (!timer)
235         {
236             timer = std::make_unique<TimerType>(
237                 sdeventplus::Event::get_default(),
238                 // **INDENT-OFF**
239                 [this](auto& /* source */) {
240                     // The timer uses the context saved on timer enable
241                     this->ConditionalCallback<CallbackAccess>::operator()(
242                         this->ctx);
243                 });
244             // **INDENT-ON**
245         }
246 
247         if (this->condition())
248         {
249             if (!timer->isEnabled())
250             {
251                 // This is the first time the condition evaluated.
252                 // Save current context for timer use.
253                 this->ctx = ctx;
254                 // Start the countdown.
255                 timer->restartOnce(delayInterval);
256             }
257         }
258         else
259         {
260             // The condition did not evaluate.  Stop the countdown.
261             timer->setEnabled(false);
262         }
263     }
264 
265   private:
266     /** @brief The length to wait for the condition to stop evaluating. */
267     std::chrono::microseconds delayInterval;
268 
269     /** @brief Delegated timer functions. */
270     std::unique_ptr<TimerType> timer = nullptr;
271 
272     /** @brief Current context for timer. */
273     Context ctx = Context::START;
274 };
275 
276 } // namespace monitoring
277 } // namespace dbus
278 } // namespace phosphor
279