blob: e3345608cbb9d827bc82b8e7eb3cb2da99bfbe7b [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
Gilad Arnoldf9f85d62014-06-19 18:07:01 -07007#include <algorithm>
David Zeuthenc1490282014-04-29 16:25:03 -07008#include <string>
9
Alex Deymo53556ec2014-03-17 10:05:57 -070010#include <base/bind.h>
David Zeuthenc1490282014-04-29 16:25:03 -070011#include <base/json/json_writer.h>
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -070012#include <base/strings/string_util.h>
David Zeuthenc1490282014-04-29 16:25:03 -070013#include <base/values.h>
14
15#include "update_engine/utils.h"
Alex Deymo53556ec2014-03-17 10:05:57 -070016
Gilad Arnold83ffdda2014-08-08 13:30:31 -070017using base::Callback;
Alex Deymo53556ec2014-03-17 10:05:57 -070018using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070019using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080020using base::TimeDelta;
Alex Deymo41a75a72014-04-15 15:36:22 -070021using chromeos_update_engine::ClockInterface;
David Zeuthenc1490282014-04-29 16:25:03 -070022using std::string;
Alex Deymo23949d42014-02-05 15:20:59 -080023
Gilad Arnolda65fced2014-07-23 09:01:31 -070024namespace {
25
26// Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
27// |ref_time| is sooner than the current value of |*reeval_time|, in which case
28// the latter is updated to the former.
29bool IsTimeGreaterThanHelper(base::Time ref_time, base::Time curr_time,
30 base::Time* reeval_time) {
31 if (curr_time > ref_time)
32 return true;
33 // Remember the nearest reference we've checked against in this evaluation.
34 if (*reeval_time > ref_time)
35 *reeval_time = ref_time;
36 return false;
37}
38
39// If |expires| never happens (maximal value), returns the maximal interval;
40// otherwise, returns the difference between |expires| and |curr|.
41TimeDelta GetTimeout(base::Time curr, base::Time expires) {
42 if (expires.is_max())
43 return TimeDelta::Max();
44 return expires - curr;
45}
46
47} // namespace
48
Alex Deymo63784a52014-05-28 10:46:14 -070049namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080050
Gilad Arnold83ffdda2014-08-08 13:30:31 -070051EvaluationContext::EvaluationContext(
52 ClockInterface* clock,
53 TimeDelta evaluation_timeout,
54 TimeDelta expiration_timeout,
55 scoped_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
Alex Deymo41a75a72014-04-15 15:36:22 -070056 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070057 evaluation_timeout_(evaluation_timeout),
Gilad Arnoldfd45a732014-08-07 15:53:46 -070058 expiration_timeout_(expiration_timeout),
Gilad Arnold83ffdda2014-08-08 13:30:31 -070059 unregister_cb_(unregister_cb.Pass()),
Alex Deymo41a75a72014-04-15 15:36:22 -070060 weak_ptr_factory_(this) {
61 ResetEvaluation();
Gilad Arnoldfd45a732014-08-07 15:53:46 -070062 ResetExpiration();
Alex Deymo41a75a72014-04-15 15:36:22 -070063}
64
Alex Deymo53556ec2014-03-17 10:05:57 -070065EvaluationContext::~EvaluationContext() {
66 RemoveObserversAndTimeout();
Gilad Arnold83ffdda2014-08-08 13:30:31 -070067 if (unregister_cb_.get())
68 unregister_cb_->Run(this);
Alex Deymo53556ec2014-03-17 10:05:57 -070069}
70
Gilad Arnold83ffdda2014-08-08 13:30:31 -070071scoped_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070072 for (auto& it : value_cache_) {
73 if (it.first->GetMode() == kVariableModeAsync)
74 it.first->RemoveObserver(this);
75 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070076 CancelMainLoopEvent(timeout_event_);
77 timeout_event_ = kEventIdNull;
Gilad Arnold83ffdda2014-08-08 13:30:31 -070078
79 return scoped_ptr<Closure>(callback_.release());
Alex Deymo53556ec2014-03-17 10:05:57 -070080}
81
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070082TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
83 if (monotonic_deadline.is_max())
84 return TimeDelta::Max();
85 TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
86 return std::max(remaining, TimeDelta());
87}
88
89Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
90 return (timeout.is_max() ? Time::Max() :
91 clock_->GetMonotonicTime() + timeout);
Alex Deymo23949d42014-02-05 15:20:59 -080092}
93
Alex Deymo53556ec2014-03-17 10:05:57 -070094void EvaluationContext::ValueChanged(BaseVariable* var) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070095 DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
96 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070097}
98
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070099void EvaluationContext::OnTimeout() {
100 DLOG(INFO) << "OnTimeout() called due to "
101 << (timeout_marks_expiration_ ? "expiration" : "poll interval");
102 timeout_event_ = kEventIdNull;
103 is_expired_ = timeout_marks_expiration_;
104 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -0700105}
106
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700107void EvaluationContext::OnValueChangedOrTimeout() {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700108 // Copy the callback handle locally, allowing it to be reassigned.
Gilad Arnold83ffdda2014-08-08 13:30:31 -0700109 scoped_ptr<Closure> callback = RemoveObserversAndTimeout();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700110
111 if (callback.get())
Gilad Arnoldfb794f42014-07-01 15:36:31 -0700112 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -0700113}
114
Gilad Arnolda65fced2014-07-23 09:01:31 -0700115bool EvaluationContext::IsWallclockTimeGreaterThan(base::Time timestamp) {
116 return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
117 &reevaluation_time_wallclock_);
118}
119
120bool EvaluationContext::IsMonotonicTimeGreaterThan(base::Time timestamp) {
121 return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
122 &reevaluation_time_monotonic_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700123}
124
125void EvaluationContext::ResetEvaluation() {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700126 evaluation_start_wallclock_ = clock_->GetWallclockTime();
127 evaluation_start_monotonic_ = clock_->GetMonotonicTime();
128 reevaluation_time_wallclock_ = Time::Max();
129 reevaluation_time_monotonic_ = Time::Max();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700130 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700131
Alex Deymo53556ec2014-03-17 10:05:57 -0700132 // Remove the cached values of non-const variables
133 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
134 if (it->first->GetMode() == kVariableModeConst) {
135 ++it;
136 } else {
137 it = value_cache_.erase(it);
138 }
139 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700140}
141
Gilad Arnoldfd45a732014-08-07 15:53:46 -0700142void EvaluationContext::ResetExpiration() {
143 expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
144 is_expired_ = false;
145}
146
Alex Deymo53556ec2014-03-17 10:05:57 -0700147bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700148 // Check that the method was not called more than once.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700149 if (callback_.get()) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700150 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
151 return false;
152 }
153
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700154 // Check that the context did not yet expire.
155 if (is_expired()) {
156 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
157 return false;
158 }
159
Gilad Arnolda65fced2014-07-23 09:01:31 -0700160 // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
161 // choose the smaller of the differences between evaluation start time and
162 // reevaluation time among the wallclock and monotonic scales.
163 TimeDelta timeout = std::min(
164 GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
165 GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700166
167 // Handle reevaluation due to async or poll variables.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700168 bool waiting_for_value_change = false;
Alex Deymo53556ec2014-03-17 10:05:57 -0700169 for (auto& it : value_cache_) {
170 switch (it.first->GetMode()) {
171 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700172 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
173 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700174 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700175 break;
176 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700177 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700178 break;
179 case kVariableModeConst:
180 // Ignored.
181 break;
182 }
183 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700184
Alex Deymo53556ec2014-03-17 10:05:57 -0700185 // Check if the re-evaluation is actually being scheduled. If there are no
186 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700187 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700188 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700189
190 // Ensure that we take into account the expiration timeout.
191 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
192 timeout_marks_expiration_ = expiration < timeout;
193 if (timeout_marks_expiration_)
194 timeout = expiration;
195
196 // Store the reevaluation callback.
197 callback_.reset(new Closure(callback));
198
199 // Schedule a timeout event, if one is set.
200 if (!timeout.is_max()) {
201 DLOG(INFO) << "Waiting for timeout in "
202 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
203 timeout_event_ = RunFromMainLoopAfterTimeout(
204 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700205 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700206 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700207 }
208
Alex Deymo53556ec2014-03-17 10:05:57 -0700209 return true;
210}
211
David Zeuthenc1490282014-04-29 16:25:03 -0700212string EvaluationContext::DumpContext() const {
213 base::DictionaryValue* variables = new base::DictionaryValue();
214 for (auto& it : value_cache_) {
215 variables->SetString(it.first->GetName(), it.second.ToString());
216 }
217
218 base::DictionaryValue value;
219 value.Set("variables", variables); // Adopts |variables|.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700220 value.SetString(
221 "evaluation_start_wallclock",
222 chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
223 value.SetString(
224 "evaluation_start_monotonic",
225 chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
David Zeuthenc1490282014-04-29 16:25:03 -0700226
227 string json_str;
228 base::JSONWriter::WriteWithOptions(&value,
229 base::JSONWriter::OPTIONS_PRETTY_PRINT,
230 &json_str);
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -0700231 base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
David Zeuthenc1490282014-04-29 16:25:03 -0700232
233 return json_str;
234}
235
Alex Deymo63784a52014-05-28 10:46:14 -0700236} // namespace chromeos_update_manager