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/platform/mutex.h"
23#include "chre/platform/system_timer.h"
24#include "chre/util/non_copyable.h"
25#include "chre/util/priority_queue.h"
26
27namespace chre {
28
29/**
30 * The type to use when referring to a timer instance.
31 *
32 * Note that this mirrors the CHRE API definition of a timer handle, so should
33 * not be changed without appropriate consideration.
34 */
35typedef uint32_t TimerHandle;
36
37//! Forward declare the EventLoop class to avoid a circular dependency between
38//! TimerPool and EventLoop.
39class EventLoop;
40
41/**
42 * Tracks requests from CHRE apps for timed events.
43 */
44class TimerPool : public NonCopyable {
45 public:
46  /**
47   * Sets up the timer instance initial conditions.
48   *
49   * @param The event loop that owns this timer.
50   */
51  TimerPool(EventLoop& eventLoop);
52
53  /**
54   * Requests a timer for a nanoapp given a cookie to pass to the nanoapp when
55   * the timer event is published.
56   *
57   * @param nanoapp The nanoapp for which this timer is being requested.
58   * @param duration The duration of the timer.
59   * @param cookie A cookie to pass to the app when the timer elapses.
60   * @param oneShot false if the timer is expected to auto-reload.
61   * @return TimerHandle of the requested timer. Returns CHRE_TIMER_INVALID if
62   *         not successful.
63   */
64  TimerHandle setTimer(const Nanoapp *nanoapp, Nanoseconds duration,
65      const void *cookie, bool oneShot);
66
67  /**
68   * Cancels a timer given a handle. If the timer handle is invalid or the timer
69   * is not owned by the passed in nanoapp, false is returned.
70   *
71   * @param nanoapp The nanoapp requesting a timer to be cancelled.
72   * @param timerHandle The handle for a timer to be cancelled.
73   * @return true if the timer was cancelled successfully.
74   */
75  bool cancelTimer(const Nanoapp* nanoapp, TimerHandle timerHandle);
76
77  // TODO: should also add methods here to:
78  //   - post an event after a delay
79  //   - invoke a callback in "unsafe" context (i.e. from other thread), which the
80  //     CHRE system could use to trigger things while the task runner is busy
81
82 private:
83  /**
84   * Tracks metadata associated with a request for a timed event.
85   */
86  struct TimerRequest {
87    //! The nanoapp instance ID from which this request was made.
88    uint32_t nanoappInstanceId;
89
90    //! The TimerHandle assigned to this request.
91    TimerHandle timerHandle;
92
93    //! The time when the request was made.
94    Nanoseconds expirationTime;
95
96    //! The requested duration of the timer.
97    Nanoseconds duration;
98
99    //! Whether or not the request is a one shot or should be rescheduled.
100    bool isOneShot;
101
102    //! The cookie pointer to be passed as an event to the requesting nanoapp.
103    const void *cookie;
104
105    /**
106     * Provides a greater than comparison of TimerRequests.
107     *
108     * @param request The other request to compare against.
109     * @return Returns true if this request is greater than the provided
110     *         request.
111     */
112    bool operator>(const TimerRequest& request) const;
113  };
114
115  //! The mutex used to lock the shared data structures below. The
116  //! handleSystemTimerCallback may be called from any context so we use a lock
117  //! to ensure exclusive access.
118  //
119  // Consider changing the design here to avoid the use of a mutex. There is
120  // another option to post an event to the system task to re-schedule the next
121  // timer. It would simplify the design and make it easier to make future
122  // extensions to this module.
123  Mutex mMutex;
124
125  //! The event loop that owns this timer pool.
126  EventLoop& mEventLoop;
127
128  //! The queue of outstanding timer requests.
129  PriorityQueue<TimerRequest, std::greater<TimerRequest>> mTimerRequests;
130
131  //! The underlying system timer used to schedule delayed callbacks.
132  SystemTimer mSystemTimer;
133
134  //! The next timer handle for generateTimerHandle() to return.
135  TimerHandle mLastTimerHandle = CHRE_TIMER_INVALID;
136
137  //! Max number of timers that can be requested for all apps
138  static constexpr size_t kMaxTimerRequests = 64;
139
140  //! Whether or not the timer handle generation logic needs to perform a
141  //! search for a vacant timer handle.
142  bool mGenerateTimerHandleMustCheckUniqueness = false;
143
144  /**
145   * Looks up a timer request given a timer handle. The lock must be acquired
146   * prior to entering this function.
147   *
148   * @param timerHandle The timer handle referring to a given request.
149   * @param index A pointer to the index of the handle. If the handle is found
150   *        this will be populated with the index of the request from the list
151   *        of requests. This is optional and will only be populated if not
152   *        nullptr.
153   * @return A pointer to a TimerRequest or nullptr if no match is found.
154   */
155  TimerRequest *getTimerRequestByTimerHandle(TimerHandle timerHandle,
156      size_t *index = nullptr);
157
158  /**
159   * Obtains a unique timer handle to return to an app requesting a timer.
160   *
161   * @return The guaranteed unique timer handle.
162   */
163  TimerHandle generateTimerHandle();
164
165  /**
166   * Obtains a unique timer handle by searching through the list of timer
167   * requests. This is a fallback for once the timer handles have been
168   * exhausted. The lock must be acquired prior to entering this function.
169   *
170   * @return A guaranteed unique timer handle.
171   */
172  TimerHandle generateUniqueTimerHandle();
173
174  /**
175   * Inserts a TimerRequest into the list of active timer requests. The order of
176   * mTimerRequests is always maintained such that the timer request with the
177   * closest expiration time is at the front of the list.
178   *
179   * @param timerRequest The timer request being inserted into the list.
180   * @return true if insertion of timer succeeds.
181   */
182   bool insertTimerRequest(const TimerRequest& timerRequest);
183
184   /**
185    * Handles a completion callback for a timer by scheduling the next timer if
186    * available. If any timers have expired already an event is posted for them
187    * as well.
188    */
189   void onSystemTimerCallback();
190
191   /**
192    * Sets the underlying system timer to the next timer in the timer list if
193    * available. The lock must be acquired prior to entering this function.
194    *
195    * @return true if any timer events were posted
196    */
197   bool handleExpiredTimersAndScheduleNext();
198
199   /**
200    * This static method handles the callback from the system timer. The data
201    * pointer here is the TimerPool instance.
202    *
203    * @param data A pointer to the timer pool.
204    */
205   static void handleSystemTimerCallback(void *timerPoolPtr);
206};
207
208}  // namespace chre
209
210#endif  // CHRE_CORE_TIMER_POOL_H_
211