• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
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 //
16 
17 #include "update_engine/update_manager/evaluation_context.h"
18 
19 #include <algorithm>
20 #include <memory>
21 #include <string>
22 #include <utility>
23 
24 #include <base/bind.h>
25 #include <base/json/json_writer.h>
26 #include <base/location.h>
27 #include <base/strings/string_util.h>
28 #include <base/values.h>
29 
30 #include "update_engine/common/system_state.h"
31 #include "update_engine/common/utils.h"
32 
33 using base::Callback;
34 using base::Closure;
35 using base::Time;
36 using base::TimeDelta;
37 using brillo::MessageLoop;
38 using chromeos_update_engine::SystemState;
39 using std::string;
40 using std::unique_ptr;
41 
42 namespace {
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.
IsTimeGreaterThanHelper(Time ref_time,Time curr_time,Time * reeval_time)47 bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time, Time* reeval_time) {
48   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|.
GetTimeout(Time curr,Time expires)58 TimeDelta GetTimeout(Time curr, Time expires) {
59   if (expires.is_max())
60     return TimeDelta::Max();
61   return expires - curr;
62 }
63 
64 }  // namespace
65 
66 namespace chromeos_update_manager {
67 
EvaluationContext(TimeDelta evaluation_timeout,TimeDelta expiration_timeout,unique_ptr<Callback<void (EvaluationContext *)>> unregister_cb)68 EvaluationContext::EvaluationContext(
69     TimeDelta evaluation_timeout,
70     TimeDelta expiration_timeout,
71     unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
72     : evaluation_timeout_(evaluation_timeout),
73       expiration_timeout_(expiration_timeout),
74       unregister_cb_(std::move(unregister_cb)),
75       weak_ptr_factory_(this) {
76   ResetEvaluation();
77   ResetExpiration();
78 }
79 
~EvaluationContext()80 EvaluationContext::~EvaluationContext() {
81   RemoveObserversAndTimeout();
82   if (unregister_cb_.get())
83     unregister_cb_->Run(this);
84 }
85 
RemoveObserversAndTimeout()86 unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
87   for (auto& it : value_cache_) {
88     if (it.first->GetMode() == kVariableModeAsync)
89       it.first->RemoveObserver(this);
90   }
91   MessageLoop::current()->CancelTask(timeout_event_);
92   timeout_event_ = MessageLoop::kTaskIdNull;
93 
94   return std::move(callback_);
95 }
96 
RemainingTime(Time monotonic_deadline) const97 TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
98   if (monotonic_deadline.is_max())
99     return TimeDelta::Max();
100   TimeDelta remaining =
101       monotonic_deadline - SystemState::Get()->clock()->GetMonotonicTime();
102   return std::max(remaining, TimeDelta());
103 }
104 
MonotonicDeadline(TimeDelta timeout)105 Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
106   return (timeout.is_max()
107               ? Time::Max()
108               : SystemState::Get()->clock()->GetMonotonicTime() + timeout);
109 }
110 
ValueChanged(BaseVariable * var)111 void EvaluationContext::ValueChanged(BaseVariable* var) {
112   DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
113   OnValueChangedOrTimeout();
114 }
115 
OnTimeout()116 void EvaluationContext::OnTimeout() {
117   DLOG(INFO) << "OnTimeout() called due to "
118              << (timeout_marks_expiration_ ? "expiration" : "poll interval");
119   timeout_event_ = MessageLoop::kTaskIdNull;
120   is_expired_ = timeout_marks_expiration_;
121   OnValueChangedOrTimeout();
122 }
123 
OnValueChangedOrTimeout()124 void EvaluationContext::OnValueChangedOrTimeout() {
125   // Copy the callback handle locally, allowing it to be reassigned.
126   unique_ptr<Closure> callback = RemoveObserversAndTimeout();
127 
128   if (callback.get())
129     callback->Run();
130 }
131 
IsWallclockTimeGreaterThan(Time timestamp)132 bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
133   return IsTimeGreaterThanHelper(
134       timestamp, evaluation_start_wallclock_, &reevaluation_time_wallclock_);
135 }
136 
IsMonotonicTimeGreaterThan(Time timestamp)137 bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
138   return IsTimeGreaterThanHelper(
139       timestamp, evaluation_start_monotonic_, &reevaluation_time_monotonic_);
140 }
141 
ResetEvaluation()142 void EvaluationContext::ResetEvaluation() {
143   const auto* clock = SystemState::Get()->clock();
144   evaluation_start_wallclock_ = clock->GetWallclockTime();
145   evaluation_start_monotonic_ = clock->GetMonotonicTime();
146   reevaluation_time_wallclock_ = Time::Max();
147   reevaluation_time_monotonic_ = Time::Max();
148   evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
149 
150   // Remove the cached values of non-const variables
151   for (auto it = value_cache_.begin(); it != value_cache_.end();) {
152     if (it->first->GetMode() == kVariableModeConst) {
153       ++it;
154     } else {
155       it = value_cache_.erase(it);
156     }
157   }
158 }
159 
ResetExpiration()160 void EvaluationContext::ResetExpiration() {
161   expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
162   is_expired_ = false;
163 }
164 
RunOnValueChangeOrTimeout(Closure callback)165 bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
166   // Check that the method was not called more than once.
167   if (callback_.get()) {
168     LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
169     return false;
170   }
171 
172   // 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 
178   // 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_));
184 
185   // Handle reevaluation due to async or poll variables.
186   bool waiting_for_value_change = false;
187   for (auto& it : value_cache_) {
188     switch (it.first->GetMode()) {
189       case kVariableModeAsync:
190         DLOG(INFO) << "Waiting for value on " << it.first->GetName();
191         it.first->AddObserver(this);
192         waiting_for_value_change = true;
193         break;
194       case kVariableModePoll:
195         timeout = std::min(timeout, it.first->GetPollInterval());
196         break;
197       case kVariableModeConst:
198         // Ignored.
199         break;
200     }
201   }
202 
203   // Check if the re-evaluation is actually being scheduled. If there are no
204   // events waited for, this function should return false.
205   if (!waiting_for_value_change && timeout.is_max())
206     return false;
207 
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);
221     timeout_event_ = MessageLoop::current()->PostDelayedTask(
222         FROM_HERE,
223         base::Bind(&EvaluationContext::OnTimeout,
224                    weak_ptr_factory_.GetWeakPtr()),
225         timeout);
226   }
227 
228   return true;
229 }
230 
DumpContext() const231 string EvaluationContext::DumpContext() const {
232   auto variables = std::make_unique<base::DictionaryValue>();
233   for (auto& it : value_cache_) {
234     variables->SetString(it.first->GetName(), it.second.ToString());
235   }
236 
237   base::DictionaryValue value;
238   value.Set("variables", std::move(variables));
239   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_));
245 
246   string json_str;
247   base::JSONWriter::WriteWithOptions(
248       value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
249   base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
250 
251   return json_str;
252 }
253 
254 }  // namespace chromeos_update_manager
255