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(const Nanoapp *nanoapp, Nanoseconds duration,
61                               const void *cookie, bool isOneShot) {
62     CHRE_ASSERT(nanoapp != nullptr);
63     return setTimer(nanoapp->getInstanceId(), duration, cookie,
64                     nullptr /* systemCallback */,
65                     SystemCallbackType::FirstCallbackType, 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 trigger
73    * a fatal error.
74    *
75    * Safe to invoke from any thread.
76    *
77    * @param duration The duration to set the timer for.
78    * @param callback The callback to invoke when the timer expires.
79    * @param callbackType The type of this callback.
80    * @param data Arbitrary data to pass to the callback. Note that extraData is
81    *        always given to the callback as nullptr.
82    * @return TimerHandle of the requested timer.
83    */
84   TimerHandle setSystemTimer(Nanoseconds duration,
85                              SystemEventCallbackFunction *callback,
86                              SystemCallbackType callbackType, void *data);
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     TimerHandle timerHandle;
118     Nanoseconds expirationTime;
119     Nanoseconds duration;
120 
121     //! The cookie pointer to be passed as an event to the requesting nanoapp,
122     //! or data pointer for system callbacks.
123     const void *cookie;
124 
125     //! If a system timer (instanceId == kSystemInstanceId), callback to invoke
126     //! after the timer expires, otherwise nullptr
127     SystemEventCallbackFunction *systemCallback;
128 
129     //! Only relevant if this is a system timer
130     SystemCallbackType callbackType;
131 
132     //! Whether or not the request is a one shot or should be rescheduled.
133     bool isOneShot;
134 
135     /**
136      * Provides a greater than comparison of TimerRequests.
137      *
138      * @param request The other request to compare against.
139      * @return Returns true if this request is greater than the provided
140      *         request.
141      */
142     bool operator>(const TimerRequest &request) const;
143   };
144 
145   //! The queue of outstanding timer requests.
146   PriorityQueue<TimerRequest, std::greater<TimerRequest>> mTimerRequests;
147 
148   //! The underlying system timer used to schedule delayed callbacks.
149   SystemTimer mSystemTimer;
150 
151   //! The next timer handle for generateTimerHandleLocked() to return.
152   TimerHandle mLastTimerHandle = CHRE_TIMER_INVALID;
153 
154   //! Max number of timers that can be requested.
155   static constexpr size_t kMaxTimerRequests = 64;
156 
157   //! The number of timers that must be available for all nanoapps
158   //! (per CHRE API).
159   static constexpr size_t kNumReservedNanoappTimers = 32;
160 
161   //! Max number of timers that can be allocated for nanoapps. Must be at least
162   //! as large as kNumReservedNanoappTimers.
163   static constexpr size_t kMaxNanoappTimers = 32;
164 
165   static_assert(kMaxNanoappTimers >= kNumReservedNanoappTimers,
166                 "Max number of nanoapp timers is too small");
167 
168   //! Whether or not the timer handle generation logic needs to perform a
169   //! search for a vacant timer handle.
170   bool mGenerateTimerHandleMustCheckUniqueness = false;
171 
172   //! The mutex to lock when using this class.
173   Mutex mMutex;
174 
175   //! The number of active nanoapp timers.
176   size_t mNumNanoappTimers = 0;
177 
178   /**
179    * Requests a timer given a cookie to pass to the CHRE event loop when the
180    * timer event is published.
181    *
182    * @param instanceId The instance ID of the caller.
183    * @param duration The duration of the timer.
184    * @param cookie A cookie to pass to the app when the timer elapses.
185    * @param systemCallback Callback to invoke (only for system-started timers).
186    * @param callbackType Identifier to pass to the callback.
187    * @param isOneShot false if the timer is expected to auto-reload.
188    * @return TimerHandle of the requested timer. Returns CHRE_TIMER_INVALID if
189    *         not successful.
190    */
191   TimerHandle setTimer(uint32_t instanceId, Nanoseconds duration,
192                        const void *cookie,
193                        SystemEventCallbackFunction *systemCallback,
194                        SystemCallbackType callbackType, bool isOneShot);
195 
196   /**
197    * Cancels a timer given a handle.
198    *
199    * @param instanceId The instance ID of the caller.
200    * @param timerHandle The handle for a timer to be cancelled.
201    * @return false if the timer handle is invalid or is not owned by the caller
202    */
203   bool cancelTimer(uint32_t instanceId, TimerHandle timerHandle);
204 
205   /**
206    * Looks up a timer request given a timer handle. mMutex must be acquired
207    * prior to calling this function.
208    *
209    * @param timerHandle The timer handle referring to a given request.
210    * @param index A pointer to the index of the handle. If the handle is found
211    *        this will be populated with the index of the request from the list
212    *        of requests. This is optional and will only be populated if not
213    *        nullptr.
214    * @return A pointer to a TimerRequest or nullptr if no match is found.
215    */
216   TimerRequest *getTimerRequestByTimerHandleLocked(TimerHandle timerHandle,
217                                                    size_t *index = nullptr);
218 
219   /**
220    * Obtains a unique timer handle to return to an app requesting a timer.
221    * mMutex must be acquired prior to calling this function.
222    *
223    * @return The guaranteed unique timer handle.
224    */
225   TimerHandle generateTimerHandleLocked();
226 
227   /**
228    * Obtains a unique timer handle by searching through the list of timer
229    * requests. This is a fallback for once the timer handles have been
230    * exhausted. mMutex must be acquired prior to calling this function.
231    *
232    * @return A guaranteed unique timer handle.
233    */
234   TimerHandle generateUniqueTimerHandleLocked();
235 
236   /**
237    * Helper function to determine whether a new timer of the specified type
238    * can be allocated. mMutex must be acquired prior to calling this function.
239    *
240    * @param isNanoappTimer true if invoked for a nanoapp timer.
241    * @return true if a new timer of the given type is allowed to be allocated.
242    */
243   bool isNewTimerAllowedLocked(bool isNanoappTimer) const;
244 
245   /**
246    * Inserts a TimerRequest into the list of active timer requests. The order of
247    * mTimerRequests is always maintained such that the timer request with the
248    * closest expiration time is at the front of the list. mMutex must be
249    * acquired prior to calling this function.
250    *
251    * @param timerRequest The timer request being inserted into the list.
252    * @return true if insertion of timer succeeds.
253    */
254   bool insertTimerRequestLocked(const TimerRequest &timerRequest);
255 
256   /**
257    * Pops the TimerRequest at the front of the list. mMutex must be acquired
258    * prior to calling this function.
259    */
260   void popTimerRequestLocked();
261 
262   /**
263    * Removes the TimerRequest at the specified index of the list. mMutex must be
264    * acquired prior to calling this function.
265    *
266    * @param index The index of the TimerRequest to remove.
267    */
268   void removeTimerRequestLocked(size_t index);
269 
270   /**
271    * Sets the underlying system timer to the next timer in the timer list if
272    * available.
273    *
274    * @return true if at least one timer had expired
275    */
276   bool handleExpiredTimersAndScheduleNext();
277 
278   /**
279    * Same as handleExpiredTimersAndScheduleNext(), except mMutex must be
280    * acquired prior to calling this function.
281    *
282    * @return true if at least one timer had expired
283    */
284   bool handleExpiredTimersAndScheduleNextLocked();
285 
286   /**
287    * This static method handles the callback from the system timer. The data
288    * pointer here is the TimerPool instance.
289    *
290    * @param data A pointer to the timer pool.
291    */
292   static void handleSystemTimerCallback(void *timerPoolPtr);
293 };
294 
295 }  // namespace chre
296 
297 #endif  // CHRE_CORE_TIMER_POOL_H_
298