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