Home | History | Annotate | Download | only in update_manager
      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/utils.h"
     31 
     32 using base::Callback;
     33 using base::Closure;
     34 using base::Time;
     35 using base::TimeDelta;
     36 using brillo::MessageLoop;
     37 using chromeos_update_engine::ClockInterface;
     38 using std::string;
     39 using std::unique_ptr;
     40 
     41 namespace {
     42 
     43 // Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
     44 // |ref_time| is sooner than the current value of |*reeval_time|, in which case
     45 // the latter is updated to the former.
     46 bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time,
     47                              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|.
     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 
     68 EvaluationContext::EvaluationContext(
     69     ClockInterface* clock,
     70     TimeDelta evaluation_timeout,
     71     TimeDelta expiration_timeout,
     72     unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
     73     : clock_(clock),
     74       evaluation_timeout_(evaluation_timeout),
     75       expiration_timeout_(expiration_timeout),
     76       unregister_cb_(std::move(unregister_cb)),
     77       weak_ptr_factory_(this) {
     78   ResetEvaluation();
     79   ResetExpiration();
     80 }
     81 
     82 EvaluationContext::~EvaluationContext() {
     83   RemoveObserversAndTimeout();
     84   if (unregister_cb_.get())
     85     unregister_cb_->Run(this);
     86 }
     87 
     88 unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
     89   for (auto& it : value_cache_) {
     90     if (it.first->GetMode() == kVariableModeAsync)
     91       it.first->RemoveObserver(this);
     92   }
     93   MessageLoop::current()->CancelTask(timeout_event_);
     94   timeout_event_ = MessageLoop::kTaskIdNull;
     95 
     96   return std::move(callback_);
     97 }
     98 
     99 TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
    100   if (monotonic_deadline.is_max())
    101     return TimeDelta::Max();
    102   TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
    103   return std::max(remaining, TimeDelta());
    104 }
    105 
    106 Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
    107   return (timeout.is_max() ? Time::Max() :
    108           clock_->GetMonotonicTime() + timeout);
    109 }
    110 
    111 void EvaluationContext::ValueChanged(BaseVariable* var) {
    112   DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
    113   OnValueChangedOrTimeout();
    114 }
    115 
    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 
    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 
    132 bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
    133   return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
    134                                  &reevaluation_time_wallclock_);
    135 }
    136 
    137 bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
    138   return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
    139                                  &reevaluation_time_monotonic_);
    140 }
    141 
    142 void EvaluationContext::ResetEvaluation() {
    143   evaluation_start_wallclock_ = clock_->GetWallclockTime();
    144   evaluation_start_monotonic_ = clock_->GetMonotonicTime();
    145   reevaluation_time_wallclock_ = Time::Max();
    146   reevaluation_time_monotonic_ = Time::Max();
    147   evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
    148 
    149   // Remove the cached values of non-const variables
    150   for (auto it = value_cache_.begin(); it != value_cache_.end(); ) {
    151     if (it->first->GetMode() == kVariableModeConst) {
    152       ++it;
    153     } else {
    154       it = value_cache_.erase(it);
    155     }
    156   }
    157 }
    158 
    159 void EvaluationContext::ResetExpiration() {
    160   expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
    161   is_expired_ = false;
    162 }
    163 
    164 bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
    165   // Check that the method was not called more than once.
    166   if (callback_.get()) {
    167     LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
    168     return false;
    169   }
    170 
    171   // Check that the context did not yet expire.
    172   if (is_expired()) {
    173     LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
    174     return false;
    175   }
    176 
    177   // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
    178   // choose the smaller of the differences between evaluation start time and
    179   // reevaluation time among the wallclock and monotonic scales.
    180   TimeDelta timeout = std::min(
    181       GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
    182       GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
    183 
    184   // Handle reevaluation due to async or poll variables.
    185   bool waiting_for_value_change = false;
    186   for (auto& it : value_cache_) {
    187     switch (it.first->GetMode()) {
    188       case kVariableModeAsync:
    189         DLOG(INFO) << "Waiting for value on " << it.first->GetName();
    190         it.first->AddObserver(this);
    191         waiting_for_value_change = true;
    192         break;
    193       case kVariableModePoll:
    194         timeout = std::min(timeout, it.first->GetPollInterval());
    195         break;
    196       case kVariableModeConst:
    197         // Ignored.
    198         break;
    199     }
    200   }
    201 
    202   // Check if the re-evaluation is actually being scheduled. If there are no
    203   // events waited for, this function should return false.
    204   if (!waiting_for_value_change && timeout.is_max())
    205     return false;
    206 
    207   // Ensure that we take into account the expiration timeout.
    208   TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
    209   timeout_marks_expiration_ = expiration < timeout;
    210   if (timeout_marks_expiration_)
    211     timeout = expiration;
    212 
    213   // Store the reevaluation callback.
    214   callback_.reset(new Closure(callback));
    215 
    216   // Schedule a timeout event, if one is set.
    217   if (!timeout.is_max()) {
    218     DLOG(INFO) << "Waiting for timeout in "
    219                << chromeos_update_engine::utils::FormatTimeDelta(timeout);
    220     timeout_event_ = MessageLoop::current()->PostDelayedTask(
    221         FROM_HERE,
    222         base::Bind(&EvaluationContext::OnTimeout,
    223                    weak_ptr_factory_.GetWeakPtr()),
    224         timeout);
    225   }
    226 
    227   return true;
    228 }
    229 
    230 string EvaluationContext::DumpContext() const {
    231   auto variables = std::make_unique<base::DictionaryValue>();
    232   for (auto& it : value_cache_) {
    233     variables->SetString(it.first->GetName(), it.second.ToString());
    234   }
    235 
    236   base::DictionaryValue value;
    237   value.Set("variables", std::move(variables));
    238   value.SetString(
    239       "evaluation_start_wallclock",
    240       chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
    241   value.SetString(
    242       "evaluation_start_monotonic",
    243       chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
    244 
    245   string json_str;
    246   base::JSONWriter::WriteWithOptions(
    247       value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
    248   base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
    249 
    250   return json_str;
    251 }
    252 
    253 }  // namespace chromeos_update_manager
    254