| // 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(); |
| |
| // Copy the callback handle locally, allowing the callback code to reset it. |
| scoped_ptr<Closure> callback(value_changed_callback_.release()); |
| if (callback.get() != NULL) |
| callback->Run(); |
| } |
| |
| 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) { |
| DLOG(INFO) |
| << "Waiting for poll timeout in " |
| << chromeos_update_engine::utils::FormatTimeDelta(reeval_timeout); |
| 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 |