blob: b86f41cf13b01a60647966b0ac86be6d657e5936 [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
Amin Hassani0468a762020-11-17 23:53:48 -080030#include "update_engine/common/system_state.h"
Alex Deymo39910dc2015-11-09 17:04:30 -080031#include "update_engine/common/utils.h"
Alex Deymo53556ec2014-03-17 10:05:57 -070032
Gilad Arnold83ffdda2014-08-08 13:30:31 -070033using base::Callback;
Alex Deymo53556ec2014-03-17 10:05:57 -070034using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070035using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080036using base::TimeDelta;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070037using brillo::MessageLoop;
Amin Hassani0468a762020-11-17 23:53:48 -080038using chromeos_update_engine::SystemState;
David Zeuthenc1490282014-04-29 16:25:03 -070039using std::string;
Ben Chan02f7c1d2014-10-18 15:18:02 -070040using std::unique_ptr;
Alex Deymo23949d42014-02-05 15:20:59 -080041
Gilad Arnolda65fced2014-07-23 09:01:31 -070042namespace {
43
44// Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
45// |ref_time| is sooner than the current value of |*reeval_time|, in which case
46// the latter is updated to the former.
Amin Hassani4b717432019-01-14 16:24:20 -080047bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time, 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(
Gilad Arnold83ffdda2014-08-08 13:30:31 -070069 TimeDelta evaluation_timeout,
70 TimeDelta expiration_timeout,
Ben Chan02f7c1d2014-10-18 15:18:02 -070071 unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
Amin Hassani0468a762020-11-17 23:53:48 -080072 : evaluation_timeout_(evaluation_timeout),
Gilad Arnoldfd45a732014-08-07 15:53:46 -070073 expiration_timeout_(expiration_timeout),
Ben Chan02f7c1d2014-10-18 15:18:02 -070074 unregister_cb_(std::move(unregister_cb)),
Alex Deymo41a75a72014-04-15 15:36:22 -070075 weak_ptr_factory_(this) {
76 ResetEvaluation();
Gilad Arnoldfd45a732014-08-07 15:53:46 -070077 ResetExpiration();
Alex Deymo41a75a72014-04-15 15:36:22 -070078}
79
Alex Deymo53556ec2014-03-17 10:05:57 -070080EvaluationContext::~EvaluationContext() {
81 RemoveObserversAndTimeout();
Gilad Arnold83ffdda2014-08-08 13:30:31 -070082 if (unregister_cb_.get())
83 unregister_cb_->Run(this);
Alex Deymo53556ec2014-03-17 10:05:57 -070084}
85
Ben Chan02f7c1d2014-10-18 15:18:02 -070086unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070087 for (auto& it : value_cache_) {
88 if (it.first->GetMode() == kVariableModeAsync)
89 it.first->RemoveObserver(this);
90 }
Alex Deymo509dd532015-06-10 14:11:05 -070091 MessageLoop::current()->CancelTask(timeout_event_);
92 timeout_event_ = MessageLoop::kTaskIdNull;
Gilad Arnold83ffdda2014-08-08 13:30:31 -070093
Alex Vakulenkoce8c8ee2016-04-08 08:59:26 -070094 return std::move(callback_);
Alex Deymo53556ec2014-03-17 10:05:57 -070095}
96
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070097TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
98 if (monotonic_deadline.is_max())
99 return TimeDelta::Max();
Amin Hassani0468a762020-11-17 23:53:48 -0800100 TimeDelta remaining =
101 monotonic_deadline - SystemState::Get()->clock()->GetMonotonicTime();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700102 return std::max(remaining, TimeDelta());
103}
104
105Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
Amin Hassani0468a762020-11-17 23:53:48 -0800106 return (timeout.is_max()
107 ? Time::Max()
108 : SystemState::Get()->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) {
Amin Hassani4b717432019-01-14 16:24:20 -0800133 return IsTimeGreaterThanHelper(
134 timestamp, evaluation_start_wallclock_, &reevaluation_time_wallclock_);
Gilad Arnolda65fced2014-07-23 09:01:31 -0700135}
136
Alex Deymof329b932014-10-30 01:37:48 -0700137bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
Amin Hassani4b717432019-01-14 16:24:20 -0800138 return IsTimeGreaterThanHelper(
139 timestamp, evaluation_start_monotonic_, &reevaluation_time_monotonic_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700140}
141
142void EvaluationContext::ResetEvaluation() {
Amin Hassani0468a762020-11-17 23:53:48 -0800143 const auto* clock = SystemState::Get()->clock();
144 evaluation_start_wallclock_ = clock->GetWallclockTime();
145 evaluation_start_monotonic_ = clock->GetMonotonicTime();
Gilad Arnolda65fced2014-07-23 09:01:31 -0700146 reevaluation_time_wallclock_ = Time::Max();
147 reevaluation_time_monotonic_ = Time::Max();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700148 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700149
Alex Deymo53556ec2014-03-17 10:05:57 -0700150 // Remove the cached values of non-const variables
Amin Hassani4b717432019-01-14 16:24:20 -0800151 for (auto it = value_cache_.begin(); it != value_cache_.end();) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700152 if (it->first->GetMode() == kVariableModeConst) {
153 ++it;
154 } else {
155 it = value_cache_.erase(it);
156 }
157 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700158}
159
Gilad Arnoldfd45a732014-08-07 15:53:46 -0700160void EvaluationContext::ResetExpiration() {
161 expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
162 is_expired_ = false;
163}
164
Alex Deymo53556ec2014-03-17 10:05:57 -0700165bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700166 // Check that the method was not called more than once.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700167 if (callback_.get()) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700168 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
169 return false;
170 }
171
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700172 // Check that the context did not yet expire.
173 if (is_expired()) {
174 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
175 return false;
176 }
177
Gilad Arnolda65fced2014-07-23 09:01:31 -0700178 // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
179 // choose the smaller of the differences between evaluation start time and
180 // reevaluation time among the wallclock and monotonic scales.
181 TimeDelta timeout = std::min(
182 GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
183 GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700184
185 // Handle reevaluation due to async or poll variables.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700186 bool waiting_for_value_change = false;
Alex Deymo53556ec2014-03-17 10:05:57 -0700187 for (auto& it : value_cache_) {
188 switch (it.first->GetMode()) {
189 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700190 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
191 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700192 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700193 break;
194 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700195 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700196 break;
197 case kVariableModeConst:
198 // Ignored.
199 break;
200 }
201 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700202
Alex Deymo53556ec2014-03-17 10:05:57 -0700203 // Check if the re-evaluation is actually being scheduled. If there are no
204 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700205 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700206 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700207
208 // Ensure that we take into account the expiration timeout.
209 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
210 timeout_marks_expiration_ = expiration < timeout;
211 if (timeout_marks_expiration_)
212 timeout = expiration;
213
214 // Store the reevaluation callback.
215 callback_.reset(new Closure(callback));
216
217 // Schedule a timeout event, if one is set.
218 if (!timeout.is_max()) {
219 DLOG(INFO) << "Waiting for timeout in "
220 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
Alex Deymo509dd532015-06-10 14:11:05 -0700221 timeout_event_ = MessageLoop::current()->PostDelayedTask(
Alex Deymo0bb23412015-06-19 00:04:46 -0700222 FROM_HERE,
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700223 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700224 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700225 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700226 }
227
Alex Deymo53556ec2014-03-17 10:05:57 -0700228 return true;
229}
230
David Zeuthenc1490282014-04-29 16:25:03 -0700231string EvaluationContext::DumpContext() const {
Ben Chanab5a0af2017-10-12 14:57:50 -0700232 auto variables = std::make_unique<base::DictionaryValue>();
David Zeuthenc1490282014-04-29 16:25:03 -0700233 for (auto& it : value_cache_) {
234 variables->SetString(it.first->GetName(), it.second.ToString());
235 }
236
237 base::DictionaryValue value;
Ben Chan42458e22017-02-07 15:33:32 -0800238 value.Set("variables", std::move(variables));
Gilad Arnolda65fced2014-07-23 09:01:31 -0700239 value.SetString(
240 "evaluation_start_wallclock",
241 chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
242 value.SetString(
243 "evaluation_start_monotonic",
244 chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
David Zeuthenc1490282014-04-29 16:25:03 -0700245
246 string json_str;
Alex Vakulenko6a9d3492015-06-15 12:53:22 -0700247 base::JSONWriter::WriteWithOptions(
248 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -0700249 base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
David Zeuthenc1490282014-04-29 16:25:03 -0700250
251 return json_str;
252}
253
Alex Deymo63784a52014-05-28 10:46:14 -0700254} // namespace chromeos_update_manager