blob: da69ced30e9c60f117effaefde9703c2b627337b [file] [log] [blame]
Alex Deymo23949d42014-02-05 15:20:59 -08001// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Deymo63784a52014-05-28 10:46:14 -07005#include "update_engine/update_manager/evaluation_context.h"
Alex Deymo23949d42014-02-05 15:20:59 -08006
David Zeuthenc1490282014-04-29 16:25:03 -07007#include <string>
8
Alex Deymo53556ec2014-03-17 10:05:57 -07009#include <base/bind.h>
David Zeuthenc1490282014-04-29 16:25:03 -070010#include <base/json/json_writer.h>
11#include <base/values.h>
12
13#include "update_engine/utils.h"
Alex Deymo53556ec2014-03-17 10:05:57 -070014
15using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070016using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080017using base::TimeDelta;
Alex Deymo41a75a72014-04-15 15:36:22 -070018using chromeos_update_engine::ClockInterface;
David Zeuthenc1490282014-04-29 16:25:03 -070019using std::string;
Alex Deymo23949d42014-02-05 15:20:59 -080020
Alex Deymo63784a52014-05-28 10:46:14 -070021namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080022
Gilad Arnoldb2271992014-06-19 12:35:24 -070023EvaluationContext::EvaluationContext(ClockInterface* clock,
24 TimeDelta evaluation_timeout)
Alex Deymo41a75a72014-04-15 15:36:22 -070025 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070026 evaluation_timeout_(evaluation_timeout),
Alex Deymo41a75a72014-04-15 15:36:22 -070027 weak_ptr_factory_(this) {
28 ResetEvaluation();
29}
30
Alex Deymo53556ec2014-03-17 10:05:57 -070031EvaluationContext::~EvaluationContext() {
32 RemoveObserversAndTimeout();
33}
34
35void EvaluationContext::RemoveObserversAndTimeout() {
36 for (auto& it : value_cache_) {
37 if (it.first->GetMode() == kVariableModeAsync)
38 it.first->RemoveObserver(this);
39 }
40 CancelMainLoopEvent(poll_timeout_event_);
41 poll_timeout_event_ = kEventIdNull;
42}
43
Alex Deymo23949d42014-02-05 15:20:59 -080044TimeDelta EvaluationContext::RemainingTime() const {
Alex Deymo41a75a72014-04-15 15:36:22 -070045 return evaluation_monotonic_deadline_ - clock_->GetMonotonicTime();
Alex Deymo23949d42014-02-05 15:20:59 -080046}
47
Alex Deymo53556ec2014-03-17 10:05:57 -070048void EvaluationContext::ValueChanged(BaseVariable* var) {
49 DLOG(INFO) << "ValueChanged for variable " << var->GetName();
50 OnValueChangedOrPollTimeout();
51}
52
53void EvaluationContext::OnPollTimeout() {
54 DLOG(INFO) << "OnPollTimeout() called.";
55 poll_timeout_event_ = kEventIdNull;
56 OnValueChangedOrPollTimeout();
57}
58
59void EvaluationContext::OnValueChangedOrPollTimeout() {
60 RemoveObserversAndTimeout();
Alex Deymo41a75a72014-04-15 15:36:22 -070061
Gilad Arnoldfb794f42014-07-01 15:36:31 -070062 // Copy the callback handle locally, allowing the callback code to reset it.
63 scoped_ptr<Closure> callback(value_changed_callback_.release());
64 if (callback.get() != NULL)
65 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -070066}
67
68bool EvaluationContext::IsTimeGreaterThan(base::Time timestamp) {
69 if (evaluation_start_ > timestamp)
70 return true;
71 // We need to keep track of these calls to trigger a reevaluation.
72 if (reevaluation_time_ > timestamp)
73 reevaluation_time_ = timestamp;
74 return false;
75}
76
77void EvaluationContext::ResetEvaluation() {
78 // It is not important if these two values are not in sync. The first value is
79 // a reference in time when the evaluation started, to device time-based
80 // values for the current evaluation. The second is a deadline for the
81 // evaluation which required a monotonic source of time.
82 evaluation_start_ = clock_->GetWallclockTime();
83 evaluation_monotonic_deadline_ =
84 clock_->GetMonotonicTime() + evaluation_timeout_;
85 reevaluation_time_ = Time::Max();
86
Alex Deymo53556ec2014-03-17 10:05:57 -070087 // Remove the cached values of non-const variables
88 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
89 if (it->first->GetMode() == kVariableModeConst) {
90 ++it;
91 } else {
92 it = value_cache_.erase(it);
93 }
94 }
Alex Deymo53556ec2014-03-17 10:05:57 -070095}
96
97bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
98 TimeDelta reeval_timeout;
99 bool reeval_timeout_set = false;
100 bool waiting_for_value_change = false;
101
Alex Deymo41a75a72014-04-15 15:36:22 -0700102 // Check if a reevaluation should be triggered due to a IsTimeGreaterThan()
103 // call.
104 if (reevaluation_time_ != Time::Max()) {
105 reeval_timeout = reevaluation_time_ - evaluation_start_;
106 reeval_timeout_set = true;
107 }
108
Alex Deymo53556ec2014-03-17 10:05:57 -0700109 if (value_changed_callback_.get() != NULL) {
110 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
111 return false;
112 }
113
114 for (auto& it : value_cache_) {
115 switch (it.first->GetMode()) {
116 case kVariableModeAsync:
117 waiting_for_value_change = true;
118 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
119 it.first->AddObserver(this);
120 break;
121 case kVariableModePoll:
122 if (!reeval_timeout_set || reeval_timeout > it.first->GetPollInterval())
123 reeval_timeout = it.first->GetPollInterval();
124 reeval_timeout_set = true;
125 break;
126 case kVariableModeConst:
127 // Ignored.
128 break;
129 }
130 }
131 // Check if the re-evaluation is actually being scheduled. If there are no
132 // events waited for, this function should return false.
133 if (!waiting_for_value_change && !reeval_timeout_set)
134 return false;
135 if (reeval_timeout_set) {
Gilad Arnoldfb794f42014-07-01 15:36:31 -0700136 DLOG(INFO)
137 << "Waiting for poll timeout in "
138 << chromeos_update_engine::utils::FormatTimeDelta(reeval_timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700139 poll_timeout_event_ = RunFromMainLoopAfterTimeout(
Alex Deymodb799532014-03-21 13:00:00 -0700140 base::Bind(&EvaluationContext::OnPollTimeout,
141 weak_ptr_factory_.GetWeakPtr()),
Alex Deymo53556ec2014-03-17 10:05:57 -0700142 reeval_timeout);
143 }
144
145 value_changed_callback_.reset(new Closure(callback));
146 return true;
147}
148
David Zeuthenc1490282014-04-29 16:25:03 -0700149string EvaluationContext::DumpContext() const {
150 base::DictionaryValue* variables = new base::DictionaryValue();
151 for (auto& it : value_cache_) {
152 variables->SetString(it.first->GetName(), it.second.ToString());
153 }
154
155 base::DictionaryValue value;
156 value.Set("variables", variables); // Adopts |variables|.
157 value.SetString("evaluation_start",
158 chromeos_update_engine::utils::ToString(evaluation_start_));
159
160 string json_str;
161 base::JSONWriter::WriteWithOptions(&value,
162 base::JSONWriter::OPTIONS_PRETTY_PRINT,
163 &json_str);
164
165 return json_str;
166}
167
Alex Deymo63784a52014-05-28 10:46:14 -0700168} // namespace chromeos_update_manager