PolicyManager: Extend the EvaluationContext to handle IsTimeGreaterThan()
The EvaluationContext needs to know the point in time where the
current evaluation of a policy request started to be able to handle
time based values like IsTimeGreaterThan() efficiently and the
timeout for the current evaluation. This patch adds such method to
the EvaluationContext class and exposes a new ResetEvaluation()
method to allow restart an evaluation. The restart of an evaluation
is independent from the notification mechanism implemented by
RunOnValueChangeOrTimeout().
The documentation on the header file is updated and extended with an
example of the workflow.
BUG=chromium:358451
TEST=Unittests added.
Change-Id: I50b6ef9d582434d6ba0f466f7e650e4a2b442249
Reviewed-on: https://chromium-review.googlesource.com/195230
Reviewed-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
diff --git a/policy_manager/evaluation_context.h b/policy_manager/evaluation_context.h
index dc7e53f..b08e296 100644
--- a/policy_manager/evaluation_context.h
+++ b/policy_manager/evaluation_context.h
@@ -12,6 +12,7 @@
#include <base/memory/weak_ptr.h>
#include <base/time/time.h>
+#include "update_engine/clock_interface.h"
#include "update_engine/policy_manager/boxed_value.h"
#include "update_engine/policy_manager/event_loop.h"
#include "update_engine/policy_manager/variable.h"
@@ -21,22 +22,59 @@
// The EvaluationContext class is the interface between a policy implementation
// and the state. The EvaluationContext tracks the variables used by a policy
// request and caches the returned values, owning those cached values.
+// The same EvaluationContext should be re-used for all the evaluations of the
+// same policy request (an AsyncPolicyRequest might involve several
+// re-evaluations). Each evaluation of the EvaluationContext is run at a given
+// point in time, which is used as a reference for the evaluation timeout and
+// the time based queries of the policy, such as IsTimeGreaterThan().
+//
+// Example:
+//
+// scoped_refptr<EvaluationContext> ec = new EvaluationContext;
+//
+// ...
+// // The following call to ResetEvaluation() is optional. Use it to reset the
+// // evaluation time if the EvaluationContext isn't used right after its
+// // construction.
+// ec->ResetEvaluation();
+// EvalStatus status = policy->SomeMethod(ec, state, &result, args...);
+//
+// ...
+// // Run a closure when any of the used async variables changes its value or
+// // the timeout for requery the values happens again.
+// ec->RunOnValueChangeOrTimeout(closure);
+// // If the provided |closure| wants to re-evaluate the policy, it should
+// // call ec->ResetEvaluation() to start a new evaluation.
+//
class EvaluationContext :
public base::RefCounted<EvaluationContext>,
private BaseVariable::ObserverInterface {
public:
- EvaluationContext() : weak_ptr_factory_(this) {}
+ explicit EvaluationContext(chromeos_update_engine::ClockInterface* clock);
~EvaluationContext();
// Returns a pointer to the value returned by the passed variable |var|. The
// EvaluationContext instance keeps the ownership of the returned object. The
- // returned object is valid during the life of the EvaluationContext, even if
- // the passed Variable changes it.
+ // returned object is valid during the life of the evaluation, even if the
+ // passed Variable changes it.
//
// In case of error, a NULL value is returned.
template<typename T>
const T* GetValue(Variable<T>* var);
+ // Returns whether the passed |timestamp| is greater than the evaluation
+ // time. The |timestamp| value should be in the same scale as the values
+ // returned by ClockInterface::GetWallclockTime().
+ bool IsTimeGreaterThan(base::Time timestamp);
+
+ // TODO(deymo): Move the following methods to an interface only visible by the
+ // PolicyManager class and not the policy implementations.
+
+ // Resets the EvaluationContext to its initial state removing all the
+ // non-const cached variables and re-setting the evaluation time. This should
+ // be called right before any new evaluation starts.
+ void ResetEvaluation();
+
// Schedules the passed |callback| closure to be called when a cached
// variable changes its value or a polling interval passes. If none of these
// events can happen, for example if there's no cached variable, this method
@@ -77,6 +115,31 @@
// Used to cancel the timeout callback.
EventId poll_timeout_event_ = kEventIdNull;
+ // Pointer to the mockable clock interface;
+ chromeos_update_engine::ClockInterface* clock_;
+
+ // The timestamp when the evaluation of this EvaluationContext started. This
+ // value is reset every time ResetEvaluation() is called. The time source
+ // used is the ClockInterface::GetWallclockTime().
+ base::Time evaluation_start_;
+
+ // The timestamp measured on the GetWallclockTime() scale, when a reevaluation
+ // should be triggered due to IsTimeGreaterThan() calls value changes. This
+ // timestamp is greater or equal to |evaluation_start_| since it is a
+ // timestamp in the future, but it can be lower than the current
+ // GetWallclockTime() at some point of the evaluation.
+ base::Time reevaluation_time_;
+
+ // The timeout of an evaluation, used to compute the RemainingTime() of an
+ // evaluation.
+ // TODO(deymo): Receive the timeout from the PolicyManager. crbug.com/363790
+ base::TimeDelta evaluation_timeout_ = base::TimeDelta::FromSeconds(5);
+
+ // The timestamp in the ClockInterface::GetMonotonicTime() scale at which the
+ // current evaluation should finish. This is used to compute the
+ // RemainingTime().
+ base::Time evaluation_monotonic_deadline_;
+
base::WeakPtrFactory<EvaluationContext> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(EvaluationContext);