1 /*
2  * Copyright (C) 2016 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 CHRE_CORE_TIMER_POOL_H_
18 #define CHRE_CORE_TIMER_POOL_H_
19 
20 #include "chre_api/chre/re.h"
21 
22 #include "chre/core/event_loop_common.h"
23 #include "chre/core/nanoapp.h"
24 #include "chre/platform/mutex.h"
25 #include "chre/platform/system_timer.h"
26 #include "chre/util/non_copyable.h"
27 #include "chre/util/priority_queue.h"
28 
29 namespace chre {
30 
31 /**
32  * The type to use when referring to a timer instance.
33  *
34  * Note that this mirrors the CHRE API definition of a timer handle, so should
35  * not be changed without appropriate consideration.
36  */
37 typedef uint32_t TimerHandle;
38 
39 /**
40  * Tracks requests from CHRE apps for timed events.
41  */
42 class TimerPool : public NonCopyable {
43  public:
44   /**
45    * Sets up the timer instance initial conditions.
46    */
47   TimerPool();
48 
49   /**
50    * Requests a timer for a nanoapp given a cookie to pass to the nanoapp when
51    * the timer event is published.
52    *
53    * @param nanoapp The nanoapp for which this timer is being requested.
54    * @param duration The duration of the timer.
55    * @param cookie A cookie to pass to the app when the timer elapses.
56    * @param isOneShot false if the timer is expected to auto-reload.
57    * @return TimerHandle of the requested timer. Returns CHRE_TIMER_INVALID if
58    *         not successful.
59    */
setNanoappTimer(const Nanoapp * nanoapp,Nanoseconds duration,const void * cookie,bool isOneShot)60   TimerHandle setNanoappTimer(
61       const Nanoapp *nanoapp, Nanoseconds duration, const void *cookie,
62       bool isOneShot) {
63     CHRE_ASSERT(nanoapp != nullptr);
64     return setTimer(nanoapp->getInstanceId(), duration, nullptr /* callback */,
65                     CHRE_EVENT_TIMER, cookie, isOneShot);
66   }
67 
68   /**
69    * Requests a timer for a system callback. When the timer expires, the
70    * specified SystemCallbackFunction will be processed in the context of the
71    * main CHRE event loop. Note that it is not immediately invoked when the
72    * timer expires. If no system timers are available, this method will fatally
73    * error.
74    *
75    * TODO: Consider adding a way to directly invoke a callback after the delay
76    *       rather than posting an event.
77    *
78    * @param duration The duration to set the timer for.
79    * @param callback The callback to invoke when the timer expires.
80    * @param callbackType The type of this callback.
81    * @param cookie A cookie to pass to this callback.
82    * @return TimerHandle of the requested timer.
83    */
84   TimerHandle setSystemTimer(
85       Nanoseconds duration, SystemCallbackFunction *callback,
86       SystemCallbackType callbackType, const void *cookie);
87 
88   /**
89    * Cancels a timer given a handle.
90    *
91    * @param nanoapp The nanoapp requesting a timer to be cancelled.
92    * @param timerHandle The handle for a timer to be cancelled.
93    * @return false if the timer handle is invalid or is not owned by the nanoapp
94    */
cancelNanoappTimer(const Nanoapp * nanoapp,TimerHandle timerHandle)95   bool cancelNanoappTimer(const Nanoapp *nanoapp, TimerHandle timerHandle) {
96     CHRE_ASSERT(nanoapp != nullptr);
97     return cancelTimer(nanoapp->getInstanceId(), timerHandle);
98   }
99 
100   /**
101    * Cancels a timer created by setSystemTimer() given a handle.
102    *
103    * @param timerHandle The handle for a timer to be cancelled.
104    * @return false if the timer handle is invalid or is not owned by the system
105    */
cancelSystemTimer(TimerHandle timerHandle)106   bool cancelSystemTimer(TimerHandle timerHandle) {
107     return cancelTimer(kSystemInstanceId, timerHandle);
108   }
109 
110  private:
111   /**
112    * Tracks metadata associated with a request for a timed event.
113    */
114   struct TimerRequest {
115     //! The instance ID from which this request was made.
116     uint32_t instanceId;
117 
118     //! The TimerHandle assigned to this request.
119     TimerHandle timerHandle;
120 
121     //! The time when the request was made.
122     Nanoseconds expirationTime;
123 
124     //! The requested duration of the timer.
125     Nanoseconds duration;
126 
127     //! The callback to invoked after the timer event is posted to CHRE.
128     SystemCallbackFunction *callback;
129 
130     //! The cookie pointer to be passed as an event to the requesting nanoapp.
131     const void *cookie;
132 
133     //! The event type to post when the timer expires.
134     uint16_t eventType;
135 
136     //! Whether or not the request is a one shot or should be rescheduled.
137     bool isOneShot;
138 
139     /**
140      * Provides a greater than comparison of TimerRequests.
141      *
142      * @param request The other request to compare against.
143      * @return Returns true if this request is greater than the provided
144      *         request.
145      */
146     bool operator>(const TimerRequest& request) const;
147   };
148 
149   //! The queue of outstanding timer requests.
150   PriorityQueue<TimerRequest, std::greater<TimerRequest>> mTimerRequests;
151 
152   //! The underlying system timer used to schedule delayed callbacks.
153   SystemTimer mSystemTimer;
154 
155   //! The next timer handle for generateTimerHandleLocked() to return.
156   TimerHandle mLastTimerHandle = CHRE_TIMER_INVALID;
157 
158   //! Max number of timers that can be requested.
159   static constexpr size_t kMaxTimerRequests = 64;
160 
161   //! The number of timers that must be available for all nanoapps
162   //! (per CHRE API).
163   static constexpr size_t kNumReservedNanoappTimers = 32;
164 
165   //! Max number of timers that can be allocated for nanoapps. Must be at least
166   //! as large as kNumReservedNanoappTimers.
167   static constexpr size_t kMaxNanoappTimers = 32;
168 
169   static_assert(kMaxNanoappTimers >= kNumReservedNanoappTimers,
170                 "Max number of nanoapp timers is too small");
171 
172   //! Whether or not the timer handle generation logic needs to perform a
173   //! search for a vacant timer handle.
174   bool mGenerateTimerHandleMustCheckUniqueness = false;
175 
176   //! The mutex to lock when using this class.
177   Mutex mMutex;
178 
179   //! The number of active nanoapp timers.
180   size_t mNumNanoappTimers = 0;
181 
182   /**
183    * Requests a timer given a cookie to pass to the CHRE event loop when the
184    * timer event is published.
185    *
186    * @param instanceId The instance ID of the caller.
187    * @param duration The duration of the timer.
188    * @param cookie A cookie to pass to the app when the timer elapses.
189    * @param isOneShot false if the timer is expected to auto-reload.
190    * @return TimerHandle of the requested timer. Returns CHRE_TIMER_INVALID if
191    *         not successful.
192    */
193   TimerHandle setTimer(
194       uint32_t instanceId, Nanoseconds duration,
195       SystemCallbackFunction *callback, uint16_t eventType,
196       const void *cookie, bool isOneShot);
197 
198   /**
199    * Cancels a timer given a handle.
200    *
201    * @param instanceId The instance ID of the caller.
202    * @param timerHandle The handle for a timer to be cancelled.
203    * @return false if the timer handle is invalid or is not owned by the caller
204    */
205   bool cancelTimer(uint32_t instanceId, TimerHandle timerHandle);
206 
207   /**
208    * Looks up a timer request given a timer handle. mMutex must be acquired
209    * prior to calling this function.
210    *
211    * @param timerHandle The timer handle referring to a given request.
212    * @param index A pointer to the index of the handle. If the handle is found
213    *        this will be populated with the index of the request from the list
214    *        of requests. This is optional and will only be populated if not
215    *        nullptr.
216    * @return A pointer to a TimerRequest or nullptr if no match is found.
217    */
218   TimerRequest *getTimerRequestByTimerHandleLocked(TimerHandle timerHandle,
219       size_t *index = nullptr);
220 
221   /**
222    * Obtains a unique timer handle to return to an app requesting a timer.
223    * mMutex must be acquired prior to calling this function.
224    *
225    * @return The guaranteed unique timer handle.
226    */
227   TimerHandle generateTimerHandleLocked();
228 
229   /**
230    * Obtains a unique timer handle by searching through the list of timer
231    * requests. This is a fallback for once the timer handles have been
232    * exhausted. mMutex must be acquired prior to calling this function.
233    *
234    * @return A guaranteed unique timer handle.
235    */
236   TimerHandle generateUniqueTimerHandleLocked();
237 
238   /**
239    * Helper function to determine whether a new timer of the specified type
240    * can be allocated. mMutex must be acquired prior to calling this function.
241    *
242    * @param isNanoappTimer true if invoked for a nanoapp timer.
243    * @return true if a new timer of the given type is allowed to be allocated.
244    */
245   bool isNewTimerAllowedLocked(bool isNanoappTimer) const;
246 
247   /**
248    * Inserts a TimerRequest into the list of active timer requests. The order of
249    * mTimerRequests is always maintained such that the timer request with the
250    * closest expiration time is at the front of the list. mMutex must be
251    * acquired prior to calling this function.
252    *
253    * @param timerRequest The timer request being inserted into the list.
254    * @return true if insertion of timer succeeds.
255    */
256   bool insertTimerRequestLocked(const TimerRequest& timerRequest);
257 
258   /**
259    * Pops the TimerRequest at the front of the list. mMutex must be acquired
260    * prior to calling this function.
261    */
262   void popTimerRequestLocked();
263 
264   /**
265    * Removes the TimerRequest at the specified index of the list. mMutex must be
266    * acquired prior to calling this function.
267    *
268    * @param index The index of the TimerRequest to remove.
269    */
270   void removeTimerRequestLocked(size_t index);
271 
272   /**
273    * Sets the underlying system timer to the next timer in the timer list if
274    * available.
275    *
276    * @return true if any timer events were posted
277    */
278   bool handleExpiredTimersAndScheduleNext();
279 
280   /**
281    * Same as handleExpiredTimersAndScheduleNext(), except mMutex must be
282    * acquired prior to calling this function.
283    *
284    * @return true if any timer events were posted
285    */
286   bool handleExpiredTimersAndScheduleNextLocked();
287 
288   /**
289    * This static method handles the callback from the system timer. The data
290    * pointer here is the TimerPool instance.
291    *
292    * @param data A pointer to the timer pool.
293    */
294   static void handleSystemTimerCallback(void *timerPoolPtr);
295 };
296 
297 }  // namespace chre
298 
299 #endif  // CHRE_CORE_TIMER_POOL_H_
300