summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad Bishop <bradleyb@fuzziesquirrel.com>2017-05-30 14:21:12 -0400
committerBrad Bishop <bradleyb@fuzziesquirrel.com>2017-06-19 16:26:48 -0400
commit3539db640767e6dafa652864fc5a5445db4ee697 (patch)
treed707f794e06cead05b7a0f3f4fca349718d374de
parentaabc54616fba1ad2c26acaf96ddf682ce3c263f0 (diff)
downloadphosphor-dbus-monitor-3539db640767e6dafa652864fc5a5445db4ee697.tar.gz
phosphor-dbus-monitor-3539db640767e6dafa652864fc5a5445db4ee697.zip
Add deferrable callbacks
Deferrable callbacks delay callback invocation until a pre configured length of time has elapsed. One example scenario where deferrable callbacks help is to avoid oscillation when testing a condition and making callbacks frequently. Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com> Change-Id: I180c99b57ec1c9bde4da76d947a026f809341c8a
-rw-r--r--src/callback.hpp78
-rw-r--r--src/example/example.yaml27
-rwxr-xr-xsrc/pdmgen.py3
-rw-r--r--src/templates/conditional.mako.cpp7
-rw-r--r--src/templates/generated.mako.hpp2
5 files changed, 111 insertions, 6 deletions
diff --git a/src/callback.hpp b/src/callback.hpp
index f7886cb..06df910 100644
--- a/src/callback.hpp
+++ b/src/callback.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <chrono>
#include "data_types.hpp"
namespace phosphor
@@ -146,14 +147,14 @@ class ConditionalCallback: public Callback
ConditionalCallback(ConditionalCallback&&) = default;
ConditionalCallback& operator=(const ConditionalCallback&) = delete;
ConditionalCallback& operator=(ConditionalCallback&&) = default;
- ~ConditionalCallback() = default;
+ virtual ~ConditionalCallback() = default;
ConditionalCallback(
const std::vector<size_t>& graphEntry,
Conditional& cond)
: graph(graphEntry), condition(cond) {}
/** @brief Run the callback if the condition is satisfied. */
- void operator()() override
+ virtual void operator()() override
{
if (condition())
{
@@ -161,7 +162,7 @@ class ConditionalCallback: public Callback
}
}
- private:
+ protected:
/** @brief The index of the callback to conditionally invoke. */
const std::vector<size_t>& graph;
@@ -169,6 +170,77 @@ class ConditionalCallback: public Callback
Conditional& condition;
};
+/** @class DeferrableCallback
+ *
+ * Deferrable callbacks wait a configurable period before
+ * invoking their associated callback.
+ *
+ * When the callback condition is initally met, start a timer. If the
+ * condition is tested again before the timer expires and it is not
+ * met cancel the timer. If the timer expires invoke the associated
+ * callback.
+ *
+ * @tparam CallbackAccess - Provide access to callback group instances.
+ * @tparam TimerType - Delegated timer access methods.
+ */
+template <typename CallbackAccess, typename TimerType>
+class DeferrableCallback : public ConditionalCallback<CallbackAccess>
+{
+ public:
+ DeferrableCallback() = delete;
+ DeferrableCallback(const DeferrableCallback&) = delete;
+ DeferrableCallback(DeferrableCallback&&) = default;
+ DeferrableCallback& operator=(const DeferrableCallback&) = delete;
+ DeferrableCallback& operator=(DeferrableCallback&&) = default;
+ ~DeferrableCallback() = default;
+
+ DeferrableCallback(
+ const std::vector<size_t>& graphEntry,
+ Conditional& cond,
+ const std::chrono::microseconds& delay)
+ : ConditionalCallback<CallbackAccess>(graphEntry, cond),
+ delayInterval(delay),
+ timer(nullptr) {}
+
+ void operator()() override
+ {
+ if (!timer)
+ {
+ timer = std::make_unique<TimerType>(
+// **INDENT-OFF**
+ [this](auto & source)
+ {
+ this->ConditionalCallback<CallbackAccess>::operator()();
+ });
+// **INDENT-ON**
+ timer->disable();
+ }
+
+ if (this->condition())
+ {
+ if (!timer->enabled())
+ {
+ // This is the first time the condition evaluated.
+ // Start the countdown.
+ timer->update(timer->now() + delayInterval);
+ timer->enable();
+ }
+ }
+ else
+ {
+ // The condition did not evaluate. Stop the countdown.
+ timer->disable();
+ }
+ }
+
+ private:
+ /** @brief The length to wait for the condition to stop evaluating. */
+ std::chrono::microseconds delayInterval;
+
+ /** @brief Delegated timer functions. */
+ std::unique_ptr<TimerType> timer;
+};
+
} // namespace monitoring
} // namespace dbus
} // namespace phosphor
diff --git a/src/example/example.yaml b/src/example/example.yaml
index d76e3ad..899c4c8 100644
--- a/src/example/example.yaml
+++ b/src/example/example.yaml
@@ -103,14 +103,13 @@
For example for a given event, one may wish to trace multiple
messages to the systemd journal. The journal callback does not
support tracing multiple messages. To do that, define a callback
- group composed of multiple journal callbacks.
+ group composed of multiple journal callbacks.'
- This example callback group only has one member. To add more, add
- additional callbacks to the members element.'
class: callback
callback: group
members:
- example journal callback
+ - example deferred condition
- name: example count condition
description: >
@@ -138,3 +137,25 @@
countbound: 3
op: '>='
bound: 115
+
+- name: example deferred condition
+ description: >
+ 'Deferred conditions operate in the same fashion as conditional callbacks
+ with the added behavior that when the condition is tested and is met,
+ invocation of the callback is deferred by the interval specified.
+
+ When the configured time has elapsed, if the condition has not been reevaluated
+ the callback is invoked.
+
+ Any condition type can be deferred in this way by setting the defer attribute.'
+
+ class: condition
+ condition: count
+ paths: example path group
+ properties: example property group
+ defer: 1000us
+ callback: example callback group
+ countop: '>='
+ countbound: 3
+ op: '>='
+ bound: 115
diff --git a/src/pdmgen.py b/src/pdmgen.py
index 5d4bf29..5e88e2c 100755
--- a/src/pdmgen.py
+++ b/src/pdmgen.py
@@ -532,6 +532,7 @@ class ConditionCallback(ConfigEntry, Renderer):
def __init__(self, *a, **kw):
self.condition = kw.pop('condition')
self.instance = kw.pop('instance')
+ self.defer = kw.pop('defer', None)
super(ConditionCallback, self).__init__(**kw)
def factory(self, objs):
@@ -580,6 +581,7 @@ class Condition(HasPropertyIndex):
def __init__(self, *a, **kw):
self.callback = kw.pop('callback')
+ self.defer = kw.pop('defer', None)
super(Condition, self).__init__(**kw)
def factory(self, objs):
@@ -592,6 +594,7 @@ class Condition(HasPropertyIndex):
'callback': 'conditional',
'instance': self.callback,
'name': self.name,
+ 'defer': self.defer
}
callback = ConditionCallback(**args)
diff --git a/src/templates/conditional.mako.cpp b/src/templates/conditional.mako.cpp
index 3cc9564..229b3d6 100644
--- a/src/templates/conditional.mako.cpp
+++ b/src/templates/conditional.mako.cpp
@@ -1,3 +1,10 @@
+% if c.defer:
+std::make_unique<DeferrableCallback<ConfigPropertyCallbacks, SDEventTimer>>(
+${indent(1)}ConfigPropertyCallbackGroups::get()[${c.graph}],
+${indent(1)}*ConfigConditions::get()[${c.condition}],
+${indent(1)}${c.defer})\
+% else:
std::make_unique<ConditionalCallback<ConfigPropertyCallbacks>>(
${indent(1)}ConfigPropertyCallbackGroups::get()[${c.graph}],
${indent(1)}*ConfigConditions::get()[${c.condition}])\
+% endif\
diff --git a/src/templates/generated.mako.hpp b/src/templates/generated.mako.hpp
index 914b065..51462b1 100644
--- a/src/templates/generated.mako.hpp
+++ b/src/templates/generated.mako.hpp
@@ -3,6 +3,7 @@
#pragma once
#include <array>
+#include <chrono>
#include <string>
#include "count.hpp"
#include "data_types.hpp"
@@ -13,6 +14,7 @@
#include "sdevent.hpp"
using namespace std::string_literals;
+using namespace std::chrono_literals;
namespace phosphor
{
OpenPOWER on IntegriCloud