blob: 2f827c1948c3304c9c45490ecc74877aa7dfb5b5 [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
Alex Deymo63784a52014-05-28 10:46:14 -070023namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080024
Gilad Arnoldb2271992014-06-19 12:35:24 -070025EvaluationContext::EvaluationContext(ClockInterface* clock,
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070026 TimeDelta evaluation_timeout,
27 TimeDelta expiration_timeout)
Alex Deymo41a75a72014-04-15 15:36:22 -070028 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070029 evaluation_timeout_(evaluation_timeout),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070030 expiration_monotonic_deadline_(MonotonicDeadline(expiration_timeout)),
Alex Deymo41a75a72014-04-15 15:36:22 -070031 weak_ptr_factory_(this) {
32 ResetEvaluation();
33}
34
Alex Deymo53556ec2014-03-17 10:05:57 -070035EvaluationContext::~EvaluationContext() {
36 RemoveObserversAndTimeout();
37}
38
39void EvaluationContext::RemoveObserversAndTimeout() {
40 for (auto& it : value_cache_) {
41 if (it.first->GetMode() == kVariableModeAsync)
42 it.first->RemoveObserver(this);
43 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070044 CancelMainLoopEvent(timeout_event_);
45 timeout_event_ = kEventIdNull;
Alex Deymo53556ec2014-03-17 10:05:57 -070046}
47
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070048TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
49 if (monotonic_deadline.is_max())
50 return TimeDelta::Max();
51 TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
52 return std::max(remaining, TimeDelta());
53}
54
55Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
56 return (timeout.is_max() ? Time::Max() :
57 clock_->GetMonotonicTime() + timeout);
Alex Deymo23949d42014-02-05 15:20:59 -080058}
59
Alex Deymo53556ec2014-03-17 10:05:57 -070060void EvaluationContext::ValueChanged(BaseVariable* var) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070061 DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
62 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070063}
64
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070065void EvaluationContext::OnTimeout() {
66 DLOG(INFO) << "OnTimeout() called due to "
67 << (timeout_marks_expiration_ ? "expiration" : "poll interval");
68 timeout_event_ = kEventIdNull;
69 is_expired_ = timeout_marks_expiration_;
70 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070071}
72
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070073void EvaluationContext::OnValueChangedOrTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070074 RemoveObserversAndTimeout();
Alex Deymo41a75a72014-04-15 15:36:22 -070075
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070076 // Copy the callback handle locally, allowing it to be reassigned.
77 scoped_ptr<Closure> callback(callback_.release());
78
79 if (callback.get())
Gilad Arnoldfb794f42014-07-01 15:36:31 -070080 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -070081}
82
83bool EvaluationContext::IsTimeGreaterThan(base::Time timestamp) {
84 if (evaluation_start_ > timestamp)
85 return true;
86 // We need to keep track of these calls to trigger a reevaluation.
87 if (reevaluation_time_ > timestamp)
88 reevaluation_time_ = timestamp;
89 return false;
90}
91
92void EvaluationContext::ResetEvaluation() {
93 // It is not important if these two values are not in sync. The first value is
94 // a reference in time when the evaluation started, to device time-based
95 // values for the current evaluation. The second is a deadline for the
96 // evaluation which required a monotonic source of time.
97 evaluation_start_ = clock_->GetWallclockTime();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070098 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -070099 reevaluation_time_ = Time::Max();
100
Alex Deymo53556ec2014-03-17 10:05:57 -0700101 // Remove the cached values of non-const variables
102 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
103 if (it->first->GetMode() == kVariableModeConst) {
104 ++it;
105 } else {
106 it = value_cache_.erase(it);
107 }
108 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700109}
110
111bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700112 // Check that the method was not called more than once.
113 if (callback_.get() != nullptr) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700114 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
115 return false;
116 }
117
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700118 // Check that the context did not yet expire.
119 if (is_expired()) {
120 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
121 return false;
122 }
123
124 TimeDelta timeout(TimeDelta::Max());
125 bool waiting_for_value_change = false;
126
127 // Handle reevaluation due to a IsTimeGreaterThan() call.
128 if (!reevaluation_time_.is_max())
129 timeout = reevaluation_time_ - evaluation_start_;
130
131 // Handle reevaluation due to async or poll variables.
Alex Deymo53556ec2014-03-17 10:05:57 -0700132 for (auto& it : value_cache_) {
133 switch (it.first->GetMode()) {
134 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700135 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
136 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700137 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700138 break;
139 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700140 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700141 break;
142 case kVariableModeConst:
143 // Ignored.
144 break;
145 }
146 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700147
Alex Deymo53556ec2014-03-17 10:05:57 -0700148 // Check if the re-evaluation is actually being scheduled. If there are no
149 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700150 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700151 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700152
153 // Ensure that we take into account the expiration timeout.
154 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
155 timeout_marks_expiration_ = expiration < timeout;
156 if (timeout_marks_expiration_)
157 timeout = expiration;
158
159 // Store the reevaluation callback.
160 callback_.reset(new Closure(callback));
161
162 // Schedule a timeout event, if one is set.
163 if (!timeout.is_max()) {
164 DLOG(INFO) << "Waiting for timeout in "
165 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
166 timeout_event_ = RunFromMainLoopAfterTimeout(
167 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700168 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700169 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700170 }
171
Alex Deymo53556ec2014-03-17 10:05:57 -0700172 return true;
173}
174
David Zeuthenc1490282014-04-29 16:25:03 -0700175string EvaluationContext::DumpContext() const {
176 base::DictionaryValue* variables = new base::DictionaryValue();
177 for (auto& it : value_cache_) {
178 variables->SetString(it.first->GetName(), it.second.ToString());
179 }
180
181 base::DictionaryValue value;
182 value.Set("variables", variables); // Adopts |variables|.
183 value.SetString("evaluation_start",
184 chromeos_update_engine::utils::ToString(evaluation_start_));
185
186 string json_str;
187 base::JSONWriter::WriteWithOptions(&value,
188 base::JSONWriter::OPTIONS_PRETTY_PRINT,
189 &json_str);
Gilad Arnold6e5ab5c2014-06-23 15:13:56 -0700190 base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
David Zeuthenc1490282014-04-29 16:25:03 -0700191
192 return json_str;
193}
194
Alex Deymo63784a52014-05-28 10:46:14 -0700195} // namespace chromeos_update_manager