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 #ifndef UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
18 #define UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
19 
20 #include <map>
21 #include <memory>
22 #include <string>
23 
24 #include <base/bind.h>
25 #include <base/callback.h>
26 #include <base/memory/ref_counted.h>
27 #include <base/memory/weak_ptr.h>
28 #include <base/time/time.h>
29 #include <brillo/message_loops/message_loop.h>
30 
31 #include "update_engine/common/clock_interface.h"
32 #include "update_engine/update_manager/boxed_value.h"
33 #include "update_engine/update_manager/variable.h"
34 
35 namespace chromeos_update_manager {
36 
37 // The EvaluationContext class is the interface between a policy implementation
38 // and the state. The EvaluationContext tracks the variables used by a policy
39 // request and caches the returned values, owning those cached values.
40 // The same EvaluationContext should be re-used for all the evaluations of the
41 // same policy request (an AsyncPolicyRequest might involve several
42 // re-evaluations). Each evaluation of the EvaluationContext is run at a given
43 // point in time, which is used as a reference for the evaluation timeout and
44 // the time based queries of the policy, such as
45 // Is{Wallclock,Monotonic}TimeGreaterThan().
46 //
47 // Example:
48 //
49 //   scoped_refptr<EvaluationContext> ec = new EvaluationContext(...);
50 //
51 //   ...
52 //   // The following call to ResetEvaluation() is optional. Use it to reset the
53 //   // evaluation time if the EvaluationContext isn't used right after its
54 //   // construction.
55 //   ec->ResetEvaluation();
56 //   EvalStatus status = policy->SomeMethod(ec, state, &result, args...);
57 //
58 //   ...
59 //   // Run a closure when any of the used async variables changes its value or
60 //   // the timeout for re-query the values happens again.
61 //   ec->RunOnValueChangeOrTimeout(closure);
62 //   // If the provided |closure| wants to re-evaluate the policy, it should
63 //   // call ec->ResetEvaluation() to start a new evaluation.
64 //
65 class EvaluationContext : public base::RefCounted<EvaluationContext>,
66                           private BaseVariable::ObserverInterface {
67  public:
68   EvaluationContext(
69       chromeos_update_engine::ClockInterface* clock,
70       base::TimeDelta evaluation_timeout,
71       base::TimeDelta expiration_timeout,
72       std::unique_ptr<base::Callback<void(EvaluationContext*)>> unregister_cb);
73   EvaluationContext(chromeos_update_engine::ClockInterface* clock,
74                     base::TimeDelta evaluation_timeout)
75       : EvaluationContext(
76             clock,
77             evaluation_timeout,
78             base::TimeDelta::Max(),
79             std::unique_ptr<base::Callback<void(EvaluationContext*)>>()) {}
80   ~EvaluationContext();
81 
82   // Returns a pointer to the value returned by the passed variable |var|. The
83   // EvaluationContext instance keeps the ownership of the returned object. The
84   // returned object is valid during the life of the evaluation, even if the
85   // passed Variable changes it.
86   //
87   // In case of error, a null value is returned.
88   template <typename T>
89   const T* GetValue(Variable<T>* var);
90 
91   // Returns whether the evaluation time has surpassed |timestamp|, on either
92   // the ClockInterface::GetWallclockTime() or
93   // ClockInterface::GetMonotonicTime() scales, respectively.
94   bool IsWallclockTimeGreaterThan(base::Time timestamp);
95   bool IsMonotonicTimeGreaterThan(base::Time timestamp);
96 
97   // Returns whether the evaluation context has expired.
98   bool is_expired() const { return is_expired_; }
99 
100   // TODO(deymo): Move the following methods to an interface only visible by the
101   // UpdateManager class and not the policy implementations.
102 
103   // Resets the EvaluationContext to its initial state removing all the
104   // non-const cached variables and re-setting the evaluation time. This should
105   // be called right before any new evaluation starts.
106   void ResetEvaluation();
107 
108   // Clears the expiration status of the EvaluationContext and resets its
109   // expiration timeout based on |expiration_timeout_|. This should be called if
110   // expiration occurred, prior to re-evaluating the policy.
111   void ResetExpiration();
112 
113   // Schedules the passed |callback| closure to be called when a cached
114   // variable changes its value, a polling interval passes, or the context
115   // expiration occurs. If none of these events can happen, for example if
116   // there's no cached variable, this method returns false.
117   //
118   // Right before the passed closure is called the EvaluationContext is
119   // reset, removing all the non-const cached values.
120   bool RunOnValueChangeOrTimeout(base::Closure callback);
121 
122   // Returns a textual representation of the evaluation context,
123   // including the variables and their values. This is intended only
124   // to help with debugging and the format may change in the future.
125   std::string DumpContext() const;
126 
127   // Removes all the Observers callbacks and timeout events scheduled by
128   // RunOnValueChangeOrTimeout(). Also releases and returns the closure
129   // associated with these events. This method is idempotent.
130   std::unique_ptr<base::Closure> RemoveObserversAndTimeout();
131 
132  private:
133   friend class UmEvaluationContextTest;
134 
135   // BaseVariable::ObserverInterface override.
136   void ValueChanged(BaseVariable* var) override;
137 
138   // Called from the main loop when a scheduled timeout has passed.
139   void OnTimeout();
140 
141   // Removes the observers from the used Variables and cancels the timeout,
142   // then executes the scheduled callback.
143   void OnValueChangedOrTimeout();
144 
145   // If |monotonic_deadline| is not Time::Max(), returns the remaining time
146   // until it is reached, or zero if it has passed. Otherwise, returns
147   // TimeDelta::Max().
148   base::TimeDelta RemainingTime(base::Time monotonic_deadline) const;
149 
150   // Returns a monotonic clock timestamp at which |timeout| will have elapsed
151   // since the current time.
152   base::Time MonotonicDeadline(base::TimeDelta timeout);
153 
154   // A map to hold the cached values for every variable.
155   typedef std::map<BaseVariable*, BoxedValue> ValueCacheMap;
156 
157   // The cached values of the called Variables.
158   ValueCacheMap value_cache_;
159 
160   // A callback used for triggering re-evaluation upon a value change or poll
161   // timeout, or notifying about the evaluation context expiration. It is up to
162   // the caller to determine whether or not expiration occurred via
163   // is_expired().
164   std::unique_ptr<base::Closure> callback_;
165 
166   // The TaskId returned by the message loop identifying the timeout callback.
167   // Used for canceling the timeout callback.
168   brillo::MessageLoop::TaskId timeout_event_ = brillo::MessageLoop::kTaskIdNull;
169 
170   // Whether a timeout event firing marks the expiration of the evaluation
171   // context.
172   bool timeout_marks_expiration_;
173 
174   // Whether the evaluation context has indeed expired.
175   bool is_expired_ = false;
176 
177   // Pointer to the mockable clock interface;
178   chromeos_update_engine::ClockInterface* const clock_;
179 
180   // The timestamps when the evaluation of this EvaluationContext started,
181   // corresponding to ClockInterface::GetWallclockTime() and
182   // ClockInterface::GetMonotonicTime(), respectively. These values are reset
183   // every time ResetEvaluation() is called.
184   base::Time evaluation_start_wallclock_;
185   base::Time evaluation_start_monotonic_;
186 
187   // The timestamps when a reevaluation should be triggered due to various
188   // expected value changes, corresponding to ClockInterface::GetWallclockTime()
189   // and ClockInterface::GetMonotonicTIme(), respectively. These timestamps are
190   // greater or equal to corresponding |evaluation_start_{wallclock,monotonic}_|
191   // counterparts since they are in the future; however, they may be smaller
192   // than the current corresponding times during the course of evaluation.
193   base::Time reevaluation_time_wallclock_;
194   base::Time reevaluation_time_monotonic_;
195 
196   // The timeout of an evaluation.
197   const base::TimeDelta evaluation_timeout_;
198 
199   // The timestamp in the ClockInterface::GetMonotonicTime() scale at which the
200   // current evaluation should finish.
201   base::Time evaluation_monotonic_deadline_;
202 
203   // The expiration timeout of the evaluation context.
204   const base::TimeDelta expiration_timeout_;
205 
206   // The monotonic clock deadline at which expiration occurs.
207   base::Time expiration_monotonic_deadline_;
208 
209   // A callback for unregistering the context upon destruction.
210   std::unique_ptr<base::Callback<void(EvaluationContext*)>> unregister_cb_;
211 
212   base::WeakPtrFactory<EvaluationContext> weak_ptr_factory_;
213 
214   DISALLOW_COPY_AND_ASSIGN(EvaluationContext);
215 };
216 
217 }  // namespace chromeos_update_manager
218 
219 // Include the implementation of the template methods.
220 #include "update_engine/update_manager/evaluation_context-inl.h"
221 
222 #endif  // UPDATE_ENGINE_UPDATE_MANAGER_EVALUATION_CONTEXT_H_
223