blob: eab0feabc66fc2ee5f8953c645991bff49f10812 [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>
12#include <base/values.h>
13
14#include "update_engine/utils.h"
Alex Deymo53556ec2014-03-17 10:05:57 -070015
16using base::Closure;
Alex Deymo41a75a72014-04-15 15:36:22 -070017using base::Time;
Alex Deymo23949d42014-02-05 15:20:59 -080018using base::TimeDelta;
Alex Deymo41a75a72014-04-15 15:36:22 -070019using chromeos_update_engine::ClockInterface;
David Zeuthenc1490282014-04-29 16:25:03 -070020using std::string;
Alex Deymo23949d42014-02-05 15:20:59 -080021
Alex Deymo63784a52014-05-28 10:46:14 -070022namespace chromeos_update_manager {
Alex Deymo23949d42014-02-05 15:20:59 -080023
Gilad Arnoldb2271992014-06-19 12:35:24 -070024EvaluationContext::EvaluationContext(ClockInterface* clock,
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070025 TimeDelta evaluation_timeout,
26 TimeDelta expiration_timeout)
Alex Deymo41a75a72014-04-15 15:36:22 -070027 : clock_(clock),
Gilad Arnoldb2271992014-06-19 12:35:24 -070028 evaluation_timeout_(evaluation_timeout),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070029 expiration_monotonic_deadline_(MonotonicDeadline(expiration_timeout)),
Alex Deymo41a75a72014-04-15 15:36:22 -070030 weak_ptr_factory_(this) {
31 ResetEvaluation();
32}
33
Alex Deymo53556ec2014-03-17 10:05:57 -070034EvaluationContext::~EvaluationContext() {
35 RemoveObserversAndTimeout();
36}
37
38void EvaluationContext::RemoveObserversAndTimeout() {
39 for (auto& it : value_cache_) {
40 if (it.first->GetMode() == kVariableModeAsync)
41 it.first->RemoveObserver(this);
42 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070043 CancelMainLoopEvent(timeout_event_);
44 timeout_event_ = kEventIdNull;
Alex Deymo53556ec2014-03-17 10:05:57 -070045}
46
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070047TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
48 if (monotonic_deadline.is_max())
49 return TimeDelta::Max();
50 TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
51 return std::max(remaining, TimeDelta());
52}
53
54Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
55 return (timeout.is_max() ? Time::Max() :
56 clock_->GetMonotonicTime() + timeout);
Alex Deymo23949d42014-02-05 15:20:59 -080057}
58
Alex Deymo53556ec2014-03-17 10:05:57 -070059void EvaluationContext::ValueChanged(BaseVariable* var) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070060 DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
61 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070062}
63
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070064void EvaluationContext::OnTimeout() {
65 DLOG(INFO) << "OnTimeout() called due to "
66 << (timeout_marks_expiration_ ? "expiration" : "poll interval");
67 timeout_event_ = kEventIdNull;
68 is_expired_ = timeout_marks_expiration_;
69 OnValueChangedOrTimeout();
Alex Deymo53556ec2014-03-17 10:05:57 -070070}
71
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070072void EvaluationContext::OnValueChangedOrTimeout() {
Alex Deymo53556ec2014-03-17 10:05:57 -070073 RemoveObserversAndTimeout();
Alex Deymo41a75a72014-04-15 15:36:22 -070074
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070075 // Copy the callback handle locally, allowing it to be reassigned.
76 scoped_ptr<Closure> callback(callback_.release());
77
78 if (callback.get())
Gilad Arnoldfb794f42014-07-01 15:36:31 -070079 callback->Run();
Alex Deymo41a75a72014-04-15 15:36:22 -070080}
81
82bool EvaluationContext::IsTimeGreaterThan(base::Time timestamp) {
83 if (evaluation_start_ > timestamp)
84 return true;
85 // We need to keep track of these calls to trigger a reevaluation.
86 if (reevaluation_time_ > timestamp)
87 reevaluation_time_ = timestamp;
88 return false;
89}
90
91void EvaluationContext::ResetEvaluation() {
92 // It is not important if these two values are not in sync. The first value is
93 // a reference in time when the evaluation started, to device time-based
94 // values for the current evaluation. The second is a deadline for the
95 // evaluation which required a monotonic source of time.
96 evaluation_start_ = clock_->GetWallclockTime();
Gilad Arnoldf9f85d62014-06-19 18:07:01 -070097 evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
Alex Deymo41a75a72014-04-15 15:36:22 -070098 reevaluation_time_ = Time::Max();
99
Alex Deymo53556ec2014-03-17 10:05:57 -0700100 // Remove the cached values of non-const variables
101 for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
102 if (it->first->GetMode() == kVariableModeConst) {
103 ++it;
104 } else {
105 it = value_cache_.erase(it);
106 }
107 }
Alex Deymo53556ec2014-03-17 10:05:57 -0700108}
109
110bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700111 // Check that the method was not called more than once.
112 if (callback_.get() != nullptr) {
Alex Deymo53556ec2014-03-17 10:05:57 -0700113 LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
114 return false;
115 }
116
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700117 // Check that the context did not yet expire.
118 if (is_expired()) {
119 LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
120 return false;
121 }
122
123 TimeDelta timeout(TimeDelta::Max());
124 bool waiting_for_value_change = false;
125
126 // Handle reevaluation due to a IsTimeGreaterThan() call.
127 if (!reevaluation_time_.is_max())
128 timeout = reevaluation_time_ - evaluation_start_;
129
130 // Handle reevaluation due to async or poll variables.
Alex Deymo53556ec2014-03-17 10:05:57 -0700131 for (auto& it : value_cache_) {
132 switch (it.first->GetMode()) {
133 case kVariableModeAsync:
Alex Deymo53556ec2014-03-17 10:05:57 -0700134 DLOG(INFO) << "Waiting for value on " << it.first->GetName();
135 it.first->AddObserver(this);
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700136 waiting_for_value_change = true;
Alex Deymo53556ec2014-03-17 10:05:57 -0700137 break;
138 case kVariableModePoll:
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700139 timeout = std::min(timeout, it.first->GetPollInterval());
Alex Deymo53556ec2014-03-17 10:05:57 -0700140 break;
141 case kVariableModeConst:
142 // Ignored.
143 break;
144 }
145 }
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700146
Alex Deymo53556ec2014-03-17 10:05:57 -0700147 // Check if the re-evaluation is actually being scheduled. If there are no
148 // events waited for, this function should return false.
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700149 if (!waiting_for_value_change && timeout.is_max())
Alex Deymo53556ec2014-03-17 10:05:57 -0700150 return false;
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700151
152 // Ensure that we take into account the expiration timeout.
153 TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
154 timeout_marks_expiration_ = expiration < timeout;
155 if (timeout_marks_expiration_)
156 timeout = expiration;
157
158 // Store the reevaluation callback.
159 callback_.reset(new Closure(callback));
160
161 // Schedule a timeout event, if one is set.
162 if (!timeout.is_max()) {
163 DLOG(INFO) << "Waiting for timeout in "
164 << chromeos_update_engine::utils::FormatTimeDelta(timeout);
165 timeout_event_ = RunFromMainLoopAfterTimeout(
166 base::Bind(&EvaluationContext::OnTimeout,
Alex Deymodb799532014-03-21 13:00:00 -0700167 weak_ptr_factory_.GetWeakPtr()),
Gilad Arnoldf9f85d62014-06-19 18:07:01 -0700168 timeout);
Alex Deymo53556ec2014-03-17 10:05:57 -0700169 }
170
Alex Deymo53556ec2014-03-17 10:05:57 -0700171 return true;
172}
173
David Zeuthenc1490282014-04-29 16:25:03 -0700174string EvaluationContext::DumpContext() const {
175 base::DictionaryValue* variables = new base::DictionaryValue();
176 for (auto& it : value_cache_) {
177 variables->SetString(it.first->GetName(), it.second.ToString());
178 }
179
180 base::DictionaryValue value;
181 value.Set("variables", variables); // Adopts |variables|.
182 value.SetString("evaluation_start",
183 chromeos_update_engine::utils::ToString(evaluation_start_));
184
185 string json_str;
186 base::JSONWriter::WriteWithOptions(&value,
187 base::JSONWriter::OPTIONS_PRETTY_PRINT,
188 &json_str);
189
190 return json_str;
191}
192
Alex Deymo63784a52014-05-28 10:46:14 -0700193} // namespace chromeos_update_manager