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/memory/ptr_util.h>
     28 #include <base/strings/string_util.h>
     29 #include <base/values.h>
     30 
     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::ClockInterface;
     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.
     47 bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time,
     48                              Time* reeval_time) {
     49   if (curr_time > ref_time)
     50     return true;
     51   // Remember the nearest reference we've checked against in this evaluation.
     52   if (*reeval_time > ref_time)
     53     *reeval_time = ref_time;
     54   return false;
     55 }
     56 
     57 // If |expires| never happens (maximal value), returns the maximal interval;
     58 // otherwise, returns the difference between |expires| and |curr|.
     59 TimeDelta GetTimeout(Time curr, Time expires) {
     60   if (expires.is_max())
     61     return TimeDelta::Max();
     62   return expires - curr;
     63 }
     64 
     65 }  // namespace
     66 
     67 namespace chromeos_update_manager {
     68 
     69 EvaluationContext::EvaluationContext(
     70     ClockInterface* clock,
     71     TimeDelta evaluation_timeout,
     72     TimeDelta expiration_timeout,
     73     unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
     74     : clock_(clock),
     75       evaluation_timeout_(evaluation_timeout),
     76       expiration_timeout_(expiration_timeout),
     77       unregister_cb_(std::move(unregister_cb)),
     78       weak_ptr_factory_(this) {
     79   ResetEvaluation();
     80   ResetExpiration();
     81 }
     82 
     83 EvaluationContext::~EvaluationContext() {
     84   RemoveObserversAndTimeout();
     85   if (unregister_cb_.get())
     86     unregister_cb_->Run(this);
     87 }
     88 
     89 unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
     90   for (auto& it : value_cache_) {
     91     if (it.first->GetMode() == kVariableModeAsync)
     92       it.first->RemoveObserver(this);
     93   }
     94   MessageLoop::current()->CancelTask(timeout_event_);
     95   timeout_event_ = MessageLoop::kTaskIdNull;
     96 
     97   return std::move(callback_);
     98 }
     99 
    100 TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
    101   if (monotonic_deadline.is_max())
    102     return TimeDelta::Max();
    103   TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
    104   return std::max(remaining, TimeDelta());
    105 }
    106 
    107 Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
    108   return (timeout.is_max() ? Time::Max() :
    109           clock_->GetMonotonicTime() + timeout);
    110 }
    111 
    112 void EvaluationContext::ValueChanged(BaseVariable* var) {
    113   DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
    114   OnValueChangedOrTimeout();
    115 }
    116 
    117 void EvaluationContext::OnTimeout() {
    118   DLOG(INFO) << "OnTimeout() called due to "
    119              << (timeout_marks_expiration_ ? "expiration" : "poll interval");
    120   timeout_event_ = MessageLoop::kTaskIdNull;
    121   is_expired_ = timeout_marks_expiration_;
    122   OnValueChangedOrTimeout();
    123 }
    124 
    125 void EvaluationContext::OnValueChangedOrTimeout() {
    126   // Copy the callback handle locally, allowing it to be reassigned.
    127   unique_ptr<Closure> callback = RemoveObserversAndTimeout();
    128 
    129   if (callback.get())
    130     callback->Run();
    131 }
    132 
    133 bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
    134   return IsTimeGreaterThanHelper(timestamp, evaluation_start_wallclock_,
    135                                  &reevaluation_time_wallclock_);
    136 }
    137 
    138 bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
    139   return IsTimeGreaterThanHelper(timestamp, evaluation_start_monotonic_,
    140                                  &reevaluation_time_monotonic_);
    141 }
    142 
    143 void EvaluationContext::ResetEvaluation() {
    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 
    160 void EvaluationContext::ResetExpiration() {
    161   expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
    162   is_expired_ = false;
    163 }
    164 
    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 
    231 string EvaluationContext::DumpContext() const {
    232   auto variables = base::MakeUnique<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