evaluation_context.cc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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. #include "update_engine/update_manager/evaluation_context.h"
  17. #include <algorithm>
  18. #include <memory>
  19. #include <string>
  20. #include <utility>
  21. #include <base/bind.h>
  22. #include <base/json/json_writer.h>
  23. #include <base/location.h>
  24. #include <base/strings/string_util.h>
  25. #include <base/values.h>
  26. #include "update_engine/common/utils.h"
  27. using base::Callback;
  28. using base::Closure;
  29. using base::Time;
  30. using base::TimeDelta;
  31. using brillo::MessageLoop;
  32. using chromeos_update_engine::ClockInterface;
  33. using std::string;
  34. using std::unique_ptr;
  35. namespace {
  36. // Returns whether |curr_time| surpassed |ref_time|; if not, also checks whether
  37. // |ref_time| is sooner than the current value of |*reeval_time|, in which case
  38. // the latter is updated to the former.
  39. bool IsTimeGreaterThanHelper(Time ref_time, Time curr_time, Time* reeval_time) {
  40. if (curr_time > ref_time)
  41. return true;
  42. // Remember the nearest reference we've checked against in this evaluation.
  43. if (*reeval_time > ref_time)
  44. *reeval_time = ref_time;
  45. return false;
  46. }
  47. // If |expires| never happens (maximal value), returns the maximal interval;
  48. // otherwise, returns the difference between |expires| and |curr|.
  49. TimeDelta GetTimeout(Time curr, Time expires) {
  50. if (expires.is_max())
  51. return TimeDelta::Max();
  52. return expires - curr;
  53. }
  54. } // namespace
  55. namespace chromeos_update_manager {
  56. EvaluationContext::EvaluationContext(
  57. ClockInterface* clock,
  58. TimeDelta evaluation_timeout,
  59. TimeDelta expiration_timeout,
  60. unique_ptr<Callback<void(EvaluationContext*)>> unregister_cb)
  61. : clock_(clock),
  62. evaluation_timeout_(evaluation_timeout),
  63. expiration_timeout_(expiration_timeout),
  64. unregister_cb_(std::move(unregister_cb)),
  65. weak_ptr_factory_(this) {
  66. ResetEvaluation();
  67. ResetExpiration();
  68. }
  69. EvaluationContext::~EvaluationContext() {
  70. RemoveObserversAndTimeout();
  71. if (unregister_cb_.get())
  72. unregister_cb_->Run(this);
  73. }
  74. unique_ptr<Closure> EvaluationContext::RemoveObserversAndTimeout() {
  75. for (auto& it : value_cache_) {
  76. if (it.first->GetMode() == kVariableModeAsync)
  77. it.first->RemoveObserver(this);
  78. }
  79. MessageLoop::current()->CancelTask(timeout_event_);
  80. timeout_event_ = MessageLoop::kTaskIdNull;
  81. return std::move(callback_);
  82. }
  83. TimeDelta EvaluationContext::RemainingTime(Time monotonic_deadline) const {
  84. if (monotonic_deadline.is_max())
  85. return TimeDelta::Max();
  86. TimeDelta remaining = monotonic_deadline - clock_->GetMonotonicTime();
  87. return std::max(remaining, TimeDelta());
  88. }
  89. Time EvaluationContext::MonotonicDeadline(TimeDelta timeout) {
  90. return (timeout.is_max() ? Time::Max()
  91. : clock_->GetMonotonicTime() + timeout);
  92. }
  93. void EvaluationContext::ValueChanged(BaseVariable* var) {
  94. DLOG(INFO) << "ValueChanged() called for variable " << var->GetName();
  95. OnValueChangedOrTimeout();
  96. }
  97. void EvaluationContext::OnTimeout() {
  98. DLOG(INFO) << "OnTimeout() called due to "
  99. << (timeout_marks_expiration_ ? "expiration" : "poll interval");
  100. timeout_event_ = MessageLoop::kTaskIdNull;
  101. is_expired_ = timeout_marks_expiration_;
  102. OnValueChangedOrTimeout();
  103. }
  104. void EvaluationContext::OnValueChangedOrTimeout() {
  105. // Copy the callback handle locally, allowing it to be reassigned.
  106. unique_ptr<Closure> callback = RemoveObserversAndTimeout();
  107. if (callback.get())
  108. callback->Run();
  109. }
  110. bool EvaluationContext::IsWallclockTimeGreaterThan(Time timestamp) {
  111. return IsTimeGreaterThanHelper(
  112. timestamp, evaluation_start_wallclock_, &reevaluation_time_wallclock_);
  113. }
  114. bool EvaluationContext::IsMonotonicTimeGreaterThan(Time timestamp) {
  115. return IsTimeGreaterThanHelper(
  116. timestamp, evaluation_start_monotonic_, &reevaluation_time_monotonic_);
  117. }
  118. void EvaluationContext::ResetEvaluation() {
  119. evaluation_start_wallclock_ = clock_->GetWallclockTime();
  120. evaluation_start_monotonic_ = clock_->GetMonotonicTime();
  121. reevaluation_time_wallclock_ = Time::Max();
  122. reevaluation_time_monotonic_ = Time::Max();
  123. evaluation_monotonic_deadline_ = MonotonicDeadline(evaluation_timeout_);
  124. // Remove the cached values of non-const variables
  125. for (auto it = value_cache_.begin(); it != value_cache_.end();) {
  126. if (it->first->GetMode() == kVariableModeConst) {
  127. ++it;
  128. } else {
  129. it = value_cache_.erase(it);
  130. }
  131. }
  132. }
  133. void EvaluationContext::ResetExpiration() {
  134. expiration_monotonic_deadline_ = MonotonicDeadline(expiration_timeout_);
  135. is_expired_ = false;
  136. }
  137. bool EvaluationContext::RunOnValueChangeOrTimeout(Closure callback) {
  138. // Check that the method was not called more than once.
  139. if (callback_.get()) {
  140. LOG(ERROR) << "RunOnValueChangeOrTimeout called more than once.";
  141. return false;
  142. }
  143. // Check that the context did not yet expire.
  144. if (is_expired()) {
  145. LOG(ERROR) << "RunOnValueChangeOrTimeout called on an expired context.";
  146. return false;
  147. }
  148. // Handle reevaluation due to a Is{Wallclock,Monotonic}TimeGreaterThan(). We
  149. // choose the smaller of the differences between evaluation start time and
  150. // reevaluation time among the wallclock and monotonic scales.
  151. TimeDelta timeout = std::min(
  152. GetTimeout(evaluation_start_wallclock_, reevaluation_time_wallclock_),
  153. GetTimeout(evaluation_start_monotonic_, reevaluation_time_monotonic_));
  154. // Handle reevaluation due to async or poll variables.
  155. bool waiting_for_value_change = false;
  156. for (auto& it : value_cache_) {
  157. switch (it.first->GetMode()) {
  158. case kVariableModeAsync:
  159. DLOG(INFO) << "Waiting for value on " << it.first->GetName();
  160. it.first->AddObserver(this);
  161. waiting_for_value_change = true;
  162. break;
  163. case kVariableModePoll:
  164. timeout = std::min(timeout, it.first->GetPollInterval());
  165. break;
  166. case kVariableModeConst:
  167. // Ignored.
  168. break;
  169. }
  170. }
  171. // Check if the re-evaluation is actually being scheduled. If there are no
  172. // events waited for, this function should return false.
  173. if (!waiting_for_value_change && timeout.is_max())
  174. return false;
  175. // Ensure that we take into account the expiration timeout.
  176. TimeDelta expiration = RemainingTime(expiration_monotonic_deadline_);
  177. timeout_marks_expiration_ = expiration < timeout;
  178. if (timeout_marks_expiration_)
  179. timeout = expiration;
  180. // Store the reevaluation callback.
  181. callback_.reset(new Closure(callback));
  182. // Schedule a timeout event, if one is set.
  183. if (!timeout.is_max()) {
  184. DLOG(INFO) << "Waiting for timeout in "
  185. << chromeos_update_engine::utils::FormatTimeDelta(timeout);
  186. timeout_event_ = MessageLoop::current()->PostDelayedTask(
  187. FROM_HERE,
  188. base::Bind(&EvaluationContext::OnTimeout,
  189. weak_ptr_factory_.GetWeakPtr()),
  190. timeout);
  191. }
  192. return true;
  193. }
  194. string EvaluationContext::DumpContext() const {
  195. auto variables = std::make_unique<base::DictionaryValue>();
  196. for (auto& it : value_cache_) {
  197. variables->SetString(it.first->GetName(), it.second.ToString());
  198. }
  199. base::DictionaryValue value;
  200. value.Set("variables", std::move(variables));
  201. value.SetString(
  202. "evaluation_start_wallclock",
  203. chromeos_update_engine::utils::ToString(evaluation_start_wallclock_));
  204. value.SetString(
  205. "evaluation_start_monotonic",
  206. chromeos_update_engine::utils::ToString(evaluation_start_monotonic_));
  207. string json_str;
  208. base::JSONWriter::WriteWithOptions(
  209. value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_str);
  210. base::TrimWhitespaceASCII(json_str, base::TRIM_TRAILING, &json_str);
  211. return json_str;
  212. }
  213. } // namespace chromeos_update_manager