blob: 2d5d7053d9c267477280813f634e40d790df7db8 [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
17using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070018using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080019using base::TimeDelta;
Alex Deymo41a75a72014-04-15 15:36:22 -070020using chromeos_update_engine::ClockInterface;
David Zeuthenc1490282014-04-29 16:25:03 -070021using std::string;
Alex Deymo23949d42014-02-05 15:20:59 -080022
Gilad Arnolda65fced2014-07-23 09:01:31 -070023namespace {
24
25// Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
26// |ref_time| is sooner than the current value of |*reeval_time|, in which case
27// the latter is updated to the former.
28bool IsTimeGreaterThanHelper(base::Time ref_time, base::Time curr_time,
29 base::Time* reeval_time) {
30 if (curr_time > ref_time)
31 return true;
32 // Remember the nearest reference we've checked against in this evaluation.
33 if (*reeval_time > ref_time)
34 *reeval_time = ref_time;
35 return false;
36}
37
38// If |expires| never happens (maximal value), returns the maximal interval;
39// otherwise, returns the difference between |expires| and |curr|.
40TimeDelta GetTimeout(base::Time curr, base::Time expires) {
41 if (expires.is_max())
42 return TimeDelta::Max();
43 return expires - curr;
44}
45
46} // namespace
47
Alex Deymo63784a52014-05-28 10:46:14 -070048namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080049
Gilad Arnoldb2271992014-06-19 12:35:24 -070050EvaluationContext::EvaluationContext(ClockInterface* clock,
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070051 TimeDelta evaluation_timeout,
52 TimeDelta expiration_timeout)
Alex Deymo41a75a72014-04-15 15:36:22 -070053 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070054 evaluation_timeout_(evaluation_timeout),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070055 expiration_monotonic_deadline_(MonotonicDeadline(expiration_timeout)),
Alex Deymo41a75a72014-04-15 15:36:22 -070056 weak_ptr_factory_(this) {
57 ResetEvaluation();
58}
59
Alex Deymo53556ec2014-03-17 10:05:57 -070060EvaluationContext::~EvaluationContext() {
61 RemoveObserversAndTimeout();
62}
63
64void EvaluationContext::RemoveObserversAndTimeout() {
65 for (auto& it : value_cache_) {
66 if (it.first->GetMode() == kVariableModeAsync)
67 it.first->RemoveObserver(this);
68 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070069 CancelMainLoopEvent(timeout_event_);
70 timeout_event_ = kEventIdNull;
Alex Deymo53556ec2014-03-17 10:05:57 -070071}
72
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070073TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
74 if (monotonic_deadline.is_max())
75 return TimeDelta::Max();
76 TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
77 return std::max(remaining, TimeDelta());
78}
79
80Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
81 return (timeout.is_max() ? Time::Max() :
82 clock_->GetMonotonicTime() + timeout);
Alex Deymo23949d42014-02-05 15:20:59 -080083}
84
Alex Deymo53556ec2014-03-17 10:05:57 -070085void EvaluationContext::ValueChanged(BaseVariable* var) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070086 DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
87 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070088}
89
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070090void EvaluationContext::OnTimeout() {
91 DLOG(INFO) << "OnTimeout() called due to "
92 << (timeout_marks_expiration_ ? "expiration" : "poll interval");
93 timeout_event_ = kEventIdNull;
94 is_expired_ = timeout_marks_expiration_;
95 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070096}
97
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070098void EvaluationContext::OnValueChangedOrTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070099 RemoveObserversAndTimeout();
Alex Deymo41a75a72014-04-15 15:36:22 -0700100
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700101 // Copy the callback handle locally, allowing it to be reassigned.
102 scoped_ptr<Closure> callback(callback_.release());
103
104 if (callback.get())
Gilad Arnoldfb794f42014-07-01 15:36:31 -0700105 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -0700106}
107
Gilad Arnolda65fced2014-07-23 09:01:31 -0700108bool EvaluationContext::IsWallclockTimeGreaterThan(base::Time timestamp) {
109 return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
110 &reevaluation_time_wallclock_);
111}
112
113bool EvaluationContext::IsMonotonicTimeGreaterThan(base::Time timestamp) {
114 return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
115 &reevaluation_time_monotonic_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700116}
117
118void EvaluationContext::ResetEvaluation() {
Gilad Arnolda65fced2014-07-23 09:01:31 -0700119 evaluation_start_wallclock_ = clock_->GetWallclockTime();
120 evaluation_start_monotonic_ = clock_->GetMonotonicTime();
121 reevaluation_time_wallclock_ = Time::Max();
122 reevaluation_time_monotonic_ = Time::Max();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700123 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -0700124
Alex Deymo53556ec2014-03-17 10:05:57 -0700125 // Remove the cached values of non-const variables
126 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
127 if (it->first->GetMode() == kVariableModeConst) {
128 ++it;
129 } else {
130 it = value_cache_.erase(it);
131 }
132 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700133}
134
135bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700136 // Check that the method was not called more than once.
137 if (callback_.get() != nullptr) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700138 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
139 return false;
140 }
141
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700142 // Check that the context did not yet expire.
143 if (is_expired()) {
144 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
145 return false;
146 }
147
Gilad Arnolda65fced2014-07-23 09:01:31 -0700148 // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
149 // choose the smaller of the differences between evaluation start time and
150 // reevaluation time among the wallclock and monotonic scales.
151 TimeDelta timeout = std::min(
152 GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
153 GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700154
155 // Handle reevaluation due to async or poll variables.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700156 bool waiting_for_value_change = false;
Alex Deymo53556ec2014-03-17 10:05:57 -0700157 for (auto& it : value_cache_) {
158 switch (it.first->GetMode()) {
159 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700160 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
161 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700162 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700163 break;
164 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700165 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700166 break;
167 case kVariableModeConst:
168 // Ignored.
169 break;
170 }
171 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700172
Alex Deymo53556ec2014-03-17 10:05:57 -0700173 // Check if the re-evaluation is actually being scheduled. If there are no
174 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700175 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700176 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700177
178 // Ensure that we take into account the expiration timeout.
179 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
180 timeout_marks_expiration_ = expiration < timeout;
181 if (timeout_marks_expiration_)
182 timeout = expiration;
183
184 // Store the reevaluation callback.
185 callback_.reset(new Closure(callback));
186
187 // Schedule a timeout event, if one is set.
188 if (!timeout.is_max()) {
189 DLOG(INFO) << "Waiting for timeout in "
190 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
191 timeout_event_ = RunFromMainLoopAfterTimeout(
192 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700193 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700194 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700195 }
196
Alex Deymo53556ec2014-03-17 10:05:57 -0700197 return true;
198}
199
David Zeuthenc1490282014-04-29 16:25:03 -0700200string EvaluationContext::DumpContext() const {
201 base::DictionaryValue* variables = new base::DictionaryValue();
202 for (auto& it : value_cache_) {
203 variables->SetString(it.first->GetName(), it.second.ToString());
204 }
205
206 base::DictionaryValue value;
207 value.Set("variables", variables); // Adopts |variables|.
Gilad Arnolda65fced2014-07-23 09:01:31 -0700208 value.SetString(
209 "evaluation_start_wallclock",
210 chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
211 value.SetString(
212 "evaluation_start_monotonic",
213 chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
David Zeuthenc1490282014-04-29 16:25:03 -0700214
215 string json_str;
216 base::JSONWriter::WriteWithOptions(&value,
217 base::JSONWriter::OPTIONS_PRETTY_PRINT,
218 &json_str);
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -0700219 base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
David Zeuthenc1490282014-04-29 16:25:03 -0700220
221 return json_str;
222}
223
Alex Deymo63784a52014-05-28 10:46:14 -0700224} // namespace chromeos_update_manager