blob: b6c7b9127f42e9d10ff958d400a8f3eb49ec5204 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2014 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
Alex Deymo23949d42014-02-05 15:20:59 -080016
Alex Deymo63784a52014-05-28 10:46:14 -070017#include "update_engine/update_manager/evaluation_context.h"
Alex Deymo23949d42014-02-05 15:20:59 -080018
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070019#include <algorithm>
Ben Chan02f7c1d2014-10-18 15:18:02 -070020#include <memory>
David Zeuthenc1490282014-04-29 16:25:03 -070021#include <string>
Ben Chan42458e22017-02-07 15:33:32 -080022#include <utility>
David Zeuthenc1490282014-04-29 16:25:03 -070023
Alex Deymo53556ec2014-03-17 10:05:57 -070024#include <base/bind.h>
David Zeuthenc1490282014-04-29 16:25:03 -070025#include <base/json/json_writer.h>
Alex Deymo0bb23412015-06-19 00:04:46 -070026#include <base/location.h>
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -070027#include <base/strings/string_util.h>
David Zeuthenc1490282014-04-29 16:25:03 -070028#include <base/values.h>
29
Alex Deymo39910dc2015-11-09 17:04:30 -080030#include "update_engine/common/utils.h"
Alex Deymo53556ec2014-03-17 10:05:57 -070031
Gilad Arnold83ffdda2014-08-08 13:30:31 -070032using base::Callback;
Alex Deymo53556ec2014-03-17 10:05:57 -070033using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070034using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080035using base::TimeDelta;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070036using brillo::MessageLoop;
Alex Deymo41a75a72014-04-15 15:36:22 -070037using chromeos_update_engine::ClockInterface;
David Zeuthenc1490282014-04-29 16:25:03 -070038using std::string;
Ben Chan02f7c1d2014-10-18 15:18:02 -070039using std::unique_ptr;
Alex Deymo23949d42014-02-05 15:20:59 -080040
Gilad Arnolda65fced2014-07-23 09:01:31 -070041namespace {
42
43// Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
44// |ref_time| is sooner than the current value of |*reeval_time|, in which case
45// the latter is updated to the former.
Alex Deymof329b932014-10-30 01:37:48 -070046bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time,
47 Time* reeval_time) {
Gilad Arnolda65fced2014-07-23 09:01:31 -070048 if (curr_time > ref_time)
49 return true;
50 // Remember the nearest reference we've checked against in this evaluation.
51 if (*reeval_time > ref_time)
52 *reeval_time = ref_time;
53 return false;
54}
55
56// If |expires| never happens (maximal value), returns the maximal interval;
57// otherwise, returns the difference between |expires| and |curr|.
Alex Deymof329b932014-10-30 01:37:48 -070058TimeDelta GetTimeout(Time curr, Time expires) {
Gilad Arnolda65fced2014-07-23 09:01:31 -070059 if (expires.is_max())
60 return TimeDelta::Max();
61 return expires - curr;
62}
63
64} // namespace
65
Alex Deymo63784a52014-05-28 10:46:14 -070066namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080067
Gilad Arnold83ffdda2014-08-08 13:30:31 -070068EvaluationContext::EvaluationContext(
69 ClockInterface* clock,
70 TimeDelta evaluation_timeout,
71 TimeDelta expiration_timeout,
Ben Chan02f7c1d2014-10-18 15:18:02 -070072 unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
Alex Deymo41a75a72014-04-15 15:36:22 -070073 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070074 evaluation_timeout_(evaluation_timeout),
Gilad Arnoldfd45a732014-08-07 15:53:46 -070075 expiration_timeout_(expiration_timeout),
Ben Chan02f7c1d2014-10-18 15:18:02 -070076 unregister_cb_(std::move(unregister_cb)),
Alex Deymo41a75a72014-04-15 15:36:22 -070077 weak_ptr_factory_(this) {
78 ResetEvaluation();
Gilad Arnoldfd45a732014-08-07 15:53:46 -070079 ResetExpiration();
Alex Deymo41a75a72014-04-15 15:36:22 -070080}
81
Alex Deymo53556ec2014-03-17 10:05:57 -070082EvaluationContext::~EvaluationContext() {
83 RemoveObserversAndTimeout();
Gilad Arnold83ffdda2014-08-08 13:30:31 -070084 if (unregister_cb_.get())
85 unregister_cb_->Run(this);
Alex Deymo53556ec2014-03-17 10:05:57 -070086}
87
Ben Chan02f7c1d2014-10-18 15:18:02 -070088unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070089 for (auto& it : value_cache_) {
90 if (it.first->GetMode() == kVariableModeAsync)
91 it.first->RemoveObserver(this);
92 }
Alex Deymo509dd532015-06-10 14:11:05 -070093 MessageLoop::current()->CancelTask(timeout_event_);
94 timeout_event_ = MessageLoop::kTaskIdNull;
Gilad Arnold83ffdda2014-08-08 13:30:31 -070095
Alex Vakulenkoce8c8ee2016-04-08 08:59:26 -070096 return std::move(callback_);
Alex Deymo53556ec2014-03-17 10:05:57 -070097}
98
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070099TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
100 if (monotonic_deadline.is_max())
101 return TimeDelta::Max();
102 TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
103 return std::max(remaining, TimeDelta());
104}
105
106Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
107 return (timeout.is_max() ? Time::Max() :
108 clock_->GetMonotonicTime() + timeout);
Alex Deymo23949d42014-02-05 15:20:59 -0800109}
110
Alex Deymo53556ec2014-03-17 10:05:57 -0700111void EvaluationContext::ValueChanged(BaseVariable* var) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700112 DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
113 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -0700114}
115
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700116void EvaluationContext::OnTimeout() {
117 DLOG(INFO) << "OnTimeout() called due to "
118 << (timeout_marks_expiration_ ? "expiration" : "poll interval");
Alex Deymo509dd532015-06-10 14:11:05 -0700119 timeout_event_ = MessageLoop::kTaskIdNull;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700120 is_expired_ = timeout_marks_expiration_;
121 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -0700122}
123
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700124void EvaluationContext::OnValueChangedOrTimeout() {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700125 // Copy the callback handle locally, allowing it to be reassigned.
Ben Chan02f7c1d2014-10-18 15:18:02 -0700126 unique_ptr<Closure> callback = RemoveObserversAndTimeout();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700127
128 if (callback.get())
Gilad Arnoldfb794f42014-07-01 15:36:31 -0700129 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -0700130}
131
Alex Deymof329b932014-10-30 01:37:48 -0700132bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700133 return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
134 &reevaluation_time_wallclock_);
135}
136
Alex Deymof329b932014-10-30 01:37:48 -0700137bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700138 return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
139 &reevaluation_time_monotonic_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700140}
141
142void EvaluationContext::ResetEvaluation() {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700143 evaluation_start_wallclock_ = clock_->GetWallclockTime();
144 evaluation_start_monotonic_ = clock_->GetMonotonicTime();
145 reevaluation_time_wallclock_ = Time::Max();
146 reevaluation_time_monotonic_ = Time::Max();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700147 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700148
Alex Deymo53556ec2014-03-17 10:05:57 -0700149 // Remove the cached values of non-const variables
150 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
151 if (it->first->GetMode() == kVariableModeConst) {
152 ++it;
153 } else {
154 it = value_cache_.erase(it);
155 }
156 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700157}
158
Gilad Arnoldfd45a732014-08-07 15:53:46 -0700159void EvaluationContext::ResetExpiration() {
160 expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
161 is_expired_ = false;
162}
163
Alex Deymo53556ec2014-03-17 10:05:57 -0700164bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700165 // Check that the method was not called more than once.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700166 if (callback_.get()) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700167 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
168 return false;
169 }
170
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700171 // Check that the context did not yet expire.
172 if (is_expired()) {
173 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
174 return false;
175 }
176
Gilad Arnolda65fced2014-07-23 09:01:31 -0700177 // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
178 // choose the smaller of the differences between evaluation start time and
179 // reevaluation time among the wallclock and monotonic scales.
180 TimeDelta timeout = std::min(
181 GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
182 GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700183
184 // Handle reevaluation due to async or poll variables.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700185 bool waiting_for_value_change = false;
Alex Deymo53556ec2014-03-17 10:05:57 -0700186 for (auto& it : value_cache_) {
187 switch (it.first->GetMode()) {
188 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700189 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
190 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700191 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700192 break;
193 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700194 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700195 break;
196 case kVariableModeConst:
197 // Ignored.
198 break;
199 }
200 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700201
Alex Deymo53556ec2014-03-17 10:05:57 -0700202 // Check if the re-evaluation is actually being scheduled. If there are no
203 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700204 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700205 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700206
207 // Ensure that we take into account the expiration timeout.
208 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
209 timeout_marks_expiration_ = expiration < timeout;
210 if (timeout_marks_expiration_)
211 timeout = expiration;
212
213 // Store the reevaluation callback.
214 callback_.reset(new Closure(callback));
215
216 // Schedule a timeout event, if one is set.
217 if (!timeout.is_max()) {
218 DLOG(INFO) << "Waiting for timeout in "
219 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
Alex Deymo509dd532015-06-10 14:11:05 -0700220 timeout_event_ = MessageLoop::current()->PostDelayedTask(
Alex Deymo0bb23412015-06-19 00:04:46 -0700221 FROM_HERE,
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700222 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700223 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700224 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700225 }
226
Alex Deymo53556ec2014-03-17 10:05:57 -0700227 return true;
228}
229
David Zeuthenc1490282014-04-29 16:25:03 -0700230string EvaluationContext::DumpContext() const {
Ben Chanab5a0af2017-10-12 14:57:50 -0700231 auto variables = std::make_unique<base::DictionaryValue>();
David Zeuthenc1490282014-04-29 16:25:03 -0700232 for (auto& it : value_cache_) {
233 variables->SetString(it.first->GetName(), it.second.ToString());
234 }
235
236 base::DictionaryValue value;
Ben Chan42458e22017-02-07 15:33:32 -0800237 value.Set("variables", std::move(variables));
Gilad Arnolda65fced2014-07-23 09:01:31 -0700238 value.SetString(
239 "evaluation_start_wallclock",
240 chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
241 value.SetString(
242 "evaluation_start_monotonic",
243 chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
David Zeuthenc1490282014-04-29 16:25:03 -0700244
245 string json_str;
Alex Vakulenko6a9d3492015-06-15 12:53:22 -0700246 base::JSONWriter::WriteWithOptions(
247 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -0700248 base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
David Zeuthenc1490282014-04-29 16:25:03 -0700249
250 return json_str;
251}
252
Alex Deymo63784a52014-05-28 10:46:14 -0700253} // namespace chromeos_update_manager