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.cc b/policy_manager/evaluation_context.cc
index 90855ad..03238cf 100644
--- a/policy_manager/evaluation_context.cc
+++ b/policy_manager/evaluation_context.cc
@@ -7,10 +7,18 @@
 #include <base/bind.h>
 
 using base::Closure;
+using base::Time;
 using base::TimeDelta;
+using chromeos_update_engine::ClockInterface;
 
 namespace chromeos_policy_manager {
 
+EvaluationContext::EvaluationContext(ClockInterface* clock)
+    : clock_(clock),
+      weak_ptr_factory_(this) {
+  ResetEvaluation();
+}
+
 EvaluationContext::~EvaluationContext() {
   RemoveObserversAndTimeout();
 }
@@ -25,9 +33,7 @@
 }
 
 TimeDelta EvaluationContext::RemainingTime() const {
-  // TODO(deymo): Return a timeout based on the elapsed time on the current
-  // policy request evaluation.
-  return TimeDelta::FromSeconds(1.);
+  return evaluation_monotonic_deadline_ - clock_->GetMonotonicTime();
 }
 
 void EvaluationContext::ValueChanged(BaseVariable* var) {
@@ -43,6 +49,32 @@
 
 void EvaluationContext::OnValueChangedOrPollTimeout() {
   RemoveObserversAndTimeout();
+
+  if (value_changed_callback_.get() != NULL) {
+    value_changed_callback_->Run();
+    value_changed_callback_.reset();
+  }
+}
+
+bool EvaluationContext::IsTimeGreaterThan(base::Time timestamp) {
+  if (evaluation_start_ > timestamp)
+    return true;
+  // We need to keep track of these calls to trigger a reevaluation.
+  if (reevaluation_time_ > timestamp)
+    reevaluation_time_ = timestamp;
+  return false;
+}
+
+void EvaluationContext::ResetEvaluation() {
+  // It is not important if these two values are not in sync. The first value is
+  // a reference in time when the evaluation started, to device time-based
+  // values for the current evaluation. The second is a deadline for the
+  // evaluation which required a monotonic source of time.
+  evaluation_start_ = clock_->GetWallclockTime();
+  evaluation_monotonic_deadline_ =
+      clock_->GetMonotonicTime() + evaluation_timeout_;
+  reevaluation_time_ = Time::Max();
+
   // Remove the cached values of non-const variables
   for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
     if (it->first->GetMode() == kVariableModeConst) {
@@ -51,11 +83,6 @@
       it = value_cache_.erase(it);
     }
   }
-
-  if (value_changed_callback_.get() != NULL) {
-    value_changed_callback_->Run();
-    value_changed_callback_.reset();
-  }
 }
 
 bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
@@ -63,6 +90,13 @@
   bool reeval_timeout_set = false;
   bool waiting_for_value_change = false;
 
+  // Check if a reevaluation should be triggered due to a IsTimeGreaterThan()
+  // call.
+  if (reevaluation_time_ != Time::Max()) {
+    reeval_timeout = reevaluation_time_ - evaluation_start_;
+    reeval_timeout_set = true;
+  }
+
   if (value_changed_callback_.get() != NULL) {
     LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
     return false;