blob: a24bf2301cb7dd5e3834ef3c678145da5808a626 [file] [log] [blame]
// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "update_engine/update_manager/evaluation_context.h"
#include <string>
#include <base/bind.h>
#include <base/json/json_writer.h>
#include <base/values.h>
#include "update_engine/utils.h"
using base::Closure;
using base::Time;
using base::TimeDelta;
using chromeos_update_engine::ClockInterface;
using std::string;
namespace chromeos_update_manager {
EvaluationContext::EvaluationContext(ClockInterface* clock,
TimeDelta evaluation_timeout)
: clock_(clock),
evaluation_timeout_(evaluation_timeout),
weak_ptr_factory_(this) {
ResetEvaluation();
}
EvaluationContext::~EvaluationContext() {
RemoveObserversAndTimeout();
}
void EvaluationContext::RemoveObserversAndTimeout() {
for (auto& it : value_cache_) {
if (it.first->GetMode() == kVariableModeAsync)
it.first->RemoveObserver(this);
}
CancelMainLoopEvent(poll_timeout_event_);
poll_timeout_event_ = kEventIdNull;
}
TimeDelta EvaluationContext::RemainingTime() const {
return evaluation_monotonic_deadline_ - clock_->GetMonotonicTime();
}
void EvaluationContext::ValueChanged(BaseVariable* var) {
DLOG(INFO) << "ValueChanged for variable " << var->GetName();
OnValueChangedOrPollTimeout();
}
void EvaluationContext::OnPollTimeout() {
DLOG(INFO) << "OnPollTimeout() called.";
poll_timeout_event_ = kEventIdNull;
OnValueChangedOrPollTimeout();
}
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) {
++it;
} else {
it = value_cache_.erase(it);
}
}
}
bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
TimeDelta reeval_timeout;
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;
}
for (auto& it : value_cache_) {
switch (it.first->GetMode()) {
case kVariableModeAsync:
waiting_for_value_change = true;
DLOG(INFO) << "Waiting for value on " << it.first->GetName();
it.first->AddObserver(this);
break;
case kVariableModePoll:
if (!reeval_timeout_set || reeval_timeout > it.first->GetPollInterval())
reeval_timeout = it.first->GetPollInterval();
reeval_timeout_set = true;
break;
case kVariableModeConst:
// Ignored.
break;
}
}
// Check if the re-evaluation is actually being scheduled. If there are no
// events waited for, this function should return false.
if (!waiting_for_value_change && !reeval_timeout_set)
return false;
if (reeval_timeout_set) {
poll_timeout_event_ = RunFromMainLoopAfterTimeout(
base::Bind(&EvaluationContext::OnPollTimeout,
weak_ptr_factory_.GetWeakPtr()),
reeval_timeout);
}
value_changed_callback_.reset(new Closure(callback));
return true;
}
string EvaluationContext::DumpContext() const {
base::DictionaryValue* variables = new base::DictionaryValue();
for (auto& it : value_cache_) {
variables->SetString(it.first->GetName(), it.second.ToString());
}
base::DictionaryValue value;
value.Set("variables", variables); // Adopts |variables|.
value.SetString("evaluation_start",
chromeos_update_engine::utils::ToString(evaluation_start_));
string json_str;
base::JSONWriter::WriteWithOptions(&value,
base::JSONWriter::OPTIONS_PRETTY_PRINT,
&json_str);
return json_str;
}
} // namespace chromeos_update_manager