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