blob: 66275deeb37bf4965d4c795ddb222b8517bb6c77 [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>
Ben Chan02f7c1d2014-10-18 15:18:02 -07008#include <memory>
David Zeuthenc1490282014-04-29 16:25:03 -07009#include <string>
10
Alex Deymo53556ec2014-03-17 10:05:57 -070011#include <base/bind.h>
David Zeuthenc1490282014-04-29 16:25:03 -070012#include <base/json/json_writer.h>
Alex Deymo0bb23412015-06-19 00:04:46 -070013#include <base/location.h>
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -070014#include <base/strings/string_util.h>
David Zeuthenc1490282014-04-29 16:25:03 -070015#include <base/values.h>
16
17#include "update_engine/utils.h"
Alex Deymo53556ec2014-03-17 10:05:57 -070018
Gilad Arnold83ffdda2014-08-08 13:30:31 -070019using base::Callback;
Alex Deymo53556ec2014-03-17 10:05:57 -070020using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070021using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080022using base::TimeDelta;
Alex Deymo509dd532015-06-10 14:11:05 -070023using chromeos::MessageLoop;
Alex Deymo41a75a72014-04-15 15:36:22 -070024using chromeos_update_engine::ClockInterface;
David Zeuthenc1490282014-04-29 16:25:03 -070025using std::string;
Ben Chan02f7c1d2014-10-18 15:18:02 -070026using std::unique_ptr;
Alex Deymo23949d42014-02-05 15:20:59 -080027
Gilad Arnolda65fced2014-07-23 09:01:31 -070028namespace {
29
30// Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
31// |ref_time| is sooner than the current value of |*reeval_time|, in which case
32// the latter is updated to the former.
Alex Deymof329b932014-10-30 01:37:48 -070033bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time,
34 Time* reeval_time) {
Gilad Arnolda65fced2014-07-23 09:01:31 -070035 if (curr_time > ref_time)
36 return true;
37 // Remember the nearest reference we've checked against in this evaluation.
38 if (*reeval_time > ref_time)
39 *reeval_time = ref_time;
40 return false;
41}
42
43// If |expires| never happens (maximal value), returns the maximal interval;
44// otherwise, returns the difference between |expires| and |curr|.
Alex Deymof329b932014-10-30 01:37:48 -070045TimeDelta GetTimeout(Time curr, Time expires) {
Gilad Arnolda65fced2014-07-23 09:01:31 -070046 if (expires.is_max())
47 return TimeDelta::Max();
48 return expires - curr;
49}
50
51} // namespace
52
Alex Deymo63784a52014-05-28 10:46:14 -070053namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080054
Gilad Arnold83ffdda2014-08-08 13:30:31 -070055EvaluationContext::EvaluationContext(
56 ClockInterface* clock,
57 TimeDelta evaluation_timeout,
58 TimeDelta expiration_timeout,
Ben Chan02f7c1d2014-10-18 15:18:02 -070059 unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
Alex Deymo41a75a72014-04-15 15:36:22 -070060 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070061 evaluation_timeout_(evaluation_timeout),
Gilad Arnoldfd45a732014-08-07 15:53:46 -070062 expiration_timeout_(expiration_timeout),
Ben Chan02f7c1d2014-10-18 15:18:02 -070063 unregister_cb_(std::move(unregister_cb)),
Alex Deymo41a75a72014-04-15 15:36:22 -070064 weak_ptr_factory_(this) {
65 ResetEvaluation();
Gilad Arnoldfd45a732014-08-07 15:53:46 -070066 ResetExpiration();
Alex Deymo41a75a72014-04-15 15:36:22 -070067}
68
Alex Deymo53556ec2014-03-17 10:05:57 -070069EvaluationContext::~EvaluationContext() {
70 RemoveObserversAndTimeout();
Gilad Arnold83ffdda2014-08-08 13:30:31 -070071 if (unregister_cb_.get())
72 unregister_cb_->Run(this);
Alex Deymo53556ec2014-03-17 10:05:57 -070073}
74
Ben Chan02f7c1d2014-10-18 15:18:02 -070075unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070076 for (auto& it : value_cache_) {
77 if (it.first->GetMode() == kVariableModeAsync)
78 it.first->RemoveObserver(this);
79 }
Alex Deymo509dd532015-06-10 14:11:05 -070080 MessageLoop::current()->CancelTask(timeout_event_);
81 timeout_event_ = MessageLoop::kTaskIdNull;
Gilad Arnold83ffdda2014-08-08 13:30:31 -070082
Ben Chan02f7c1d2014-10-18 15:18:02 -070083 return unique_ptr<Closure>(callback_.release());
Alex Deymo53556ec2014-03-17 10:05:57 -070084}
85
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070086TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
87 if (monotonic_deadline.is_max())
88 return TimeDelta::Max();
89 TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
90 return std::max(remaining, TimeDelta());
91}
92
93Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
94 return (timeout.is_max() ? Time::Max() :
95 clock_->GetMonotonicTime() + timeout);
Alex Deymo23949d42014-02-05 15:20:59 -080096}
97
Alex Deymo53556ec2014-03-17 10:05:57 -070098void EvaluationContext::ValueChanged(BaseVariable* var) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070099 DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
100 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -0700101}
102
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700103void EvaluationContext::OnTimeout() {
104 DLOG(INFO) << "OnTimeout() called due to "
105 << (timeout_marks_expiration_ ? "expiration" : "poll interval");
Alex Deymo509dd532015-06-10 14:11:05 -0700106 timeout_event_ = MessageLoop::kTaskIdNull;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700107 is_expired_ = timeout_marks_expiration_;
108 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -0700109}
110
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700111void EvaluationContext::OnValueChangedOrTimeout() {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700112 // Copy the callback handle locally, allowing it to be reassigned.
Ben Chan02f7c1d2014-10-18 15:18:02 -0700113 unique_ptr<Closure> callback = RemoveObserversAndTimeout();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700114
115 if (callback.get())
Gilad Arnoldfb794f42014-07-01 15:36:31 -0700116 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -0700117}
118
Alex Deymof329b932014-10-30 01:37:48 -0700119bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700120 return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
121 &reevaluation_time_wallclock_);
122}
123
Alex Deymof329b932014-10-30 01:37:48 -0700124bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700125 return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
126 &reevaluation_time_monotonic_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700127}
128
129void EvaluationContext::ResetEvaluation() {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700130 evaluation_start_wallclock_ = clock_->GetWallclockTime();
131 evaluation_start_monotonic_ = clock_->GetMonotonicTime();
132 reevaluation_time_wallclock_ = Time::Max();
133 reevaluation_time_monotonic_ = Time::Max();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700134 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700135
Alex Deymo53556ec2014-03-17 10:05:57 -0700136 // Remove the cached values of non-const variables
137 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
138 if (it->first->GetMode() == kVariableModeConst) {
139 ++it;
140 } else {
141 it = value_cache_.erase(it);
142 }
143 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700144}
145
Gilad Arnoldfd45a732014-08-07 15:53:46 -0700146void EvaluationContext::ResetExpiration() {
147 expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
148 is_expired_ = false;
149}
150
Alex Deymo53556ec2014-03-17 10:05:57 -0700151bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700152 // Check that the method was not called more than once.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700153 if (callback_.get()) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700154 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
155 return false;
156 }
157
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700158 // Check that the context did not yet expire.
159 if (is_expired()) {
160 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
161 return false;
162 }
163
Gilad Arnolda65fced2014-07-23 09:01:31 -0700164 // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
165 // choose the smaller of the differences between evaluation start time and
166 // reevaluation time among the wallclock and monotonic scales.
167 TimeDelta timeout = std::min(
168 GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
169 GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700170
171 // Handle reevaluation due to async or poll variables.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700172 bool waiting_for_value_change = false;
Alex Deymo53556ec2014-03-17 10:05:57 -0700173 for (auto& it : value_cache_) {
174 switch (it.first->GetMode()) {
175 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700176 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
177 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700178 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700179 break;
180 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700181 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700182 break;
183 case kVariableModeConst:
184 // Ignored.
185 break;
186 }
187 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700188
Alex Deymo53556ec2014-03-17 10:05:57 -0700189 // Check if the re-evaluation is actually being scheduled. If there are no
190 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700191 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700192 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700193
194 // Ensure that we take into account the expiration timeout.
195 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
196 timeout_marks_expiration_ = expiration < timeout;
197 if (timeout_marks_expiration_)
198 timeout = expiration;
199
200 // Store the reevaluation callback.
201 callback_.reset(new Closure(callback));
202
203 // Schedule a timeout event, if one is set.
204 if (!timeout.is_max()) {
205 DLOG(INFO) << "Waiting for timeout in "
206 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
Alex Deymo509dd532015-06-10 14:11:05 -0700207 timeout_event_ = MessageLoop::current()->PostDelayedTask(
Alex Deymo0bb23412015-06-19 00:04:46 -0700208 FROM_HERE,
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700209 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700210 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700211 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700212 }
213
Alex Deymo53556ec2014-03-17 10:05:57 -0700214 return true;
215}
216
David Zeuthenc1490282014-04-29 16:25:03 -0700217string EvaluationContext::DumpContext() const {
218 base::DictionaryValue* variables = new base::DictionaryValue();
219 for (auto& it : value_cache_) {
220 variables->SetString(it.first->GetName(), it.second.ToString());
221 }
222
223 base::DictionaryValue value;
224 value.Set("variables", variables); // Adopts |variables|.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700225 value.SetString(
226 "evaluation_start_wallclock",
227 chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
228 value.SetString(
229 "evaluation_start_monotonic",
230 chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
David Zeuthenc1490282014-04-29 16:25:03 -0700231
232 string json_str;
Alex Vakulenko6a9d3492015-06-15 12:53:22 -0700233 base::JSONWriter::WriteWithOptions(
234 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -0700235 base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
David Zeuthenc1490282014-04-29 16:25:03 -0700236
237 return json_str;
238}
239
Alex Deymo63784a52014-05-28 10:46:14 -0700240} // namespace chromeos_update_manager