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 #include "chre/core/event_loop.h"
18 #include "chre/core/event_loop_manager.h"
19 #include "chre/core/timer_pool.h"
20 #include "chre/platform/fatal_error.h"
21 #include "chre/platform/system_time.h"
22 #include "chre/util/lock_guard.h"
23
24 namespace chre {
25
TimerPool()26 TimerPool::TimerPool() {
27 if (!mSystemTimer.init()) {
28 FATAL_ERROR("Failed to initialize a system timer for the TimerPool");
29 }
30 }
31
setSystemTimer(Nanoseconds duration,SystemCallbackFunction * callback,SystemCallbackType callbackType,const void * cookie)32 TimerHandle TimerPool::setSystemTimer(
33 Nanoseconds duration, SystemCallbackFunction *callback,
34 SystemCallbackType callbackType, const void *cookie) {
35 TimerHandle timerHandle = setTimer(
36 kSystemInstanceId, duration, callback,
37 static_cast<uint16_t>(callbackType), cookie, true /* isOneShot */);
38
39 if (timerHandle == CHRE_TIMER_INVALID) {
40 FATAL_ERROR("Failed to set system timer");
41 }
42
43 return timerHandle;
44 }
45
setTimer(uint32_t instanceId,Nanoseconds duration,SystemCallbackFunction * callback,uint16_t eventType,const void * cookie,bool isOneShot)46 TimerHandle TimerPool::setTimer(
47 uint32_t instanceId, Nanoseconds duration,
48 SystemCallbackFunction *callback, uint16_t eventType,
49 const void *cookie, bool isOneShot) {
50 LockGuard<Mutex> lock(mMutex);
51
52 TimerRequest timerRequest;
53 timerRequest.instanceId = instanceId;
54 timerRequest.timerHandle = generateTimerHandleLocked();
55 timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration;
56 timerRequest.duration = duration;
57 timerRequest.isOneShot = isOneShot;
58 timerRequest.callback = callback;
59 timerRequest.eventType = eventType;
60 timerRequest.cookie = cookie;
61
62 bool newTimerExpiresEarliest =
63 (!mTimerRequests.empty() && mTimerRequests.top() > timerRequest);
64 bool success = insertTimerRequestLocked(timerRequest);
65
66 if (success) {
67 if (newTimerExpiresEarliest) {
68 mSystemTimer.set(handleSystemTimerCallback, this, duration);
69 } else if (mTimerRequests.size() == 1) {
70 // If this timer request was the first, schedule it.
71 handleExpiredTimersAndScheduleNextLocked();
72 }
73 }
74
75 return success ? timerRequest.timerHandle : CHRE_TIMER_INVALID;
76 }
77
cancelTimer(uint32_t instanceId,TimerHandle timerHandle)78 bool TimerPool::cancelTimer(
79 uint32_t instanceId, TimerHandle timerHandle) {
80 LockGuard<Mutex> lock(mMutex);
81 size_t index;
82 bool success = false;
83 TimerRequest *timerRequest = getTimerRequestByTimerHandleLocked(timerHandle,
84 &index);
85
86 if (timerRequest == nullptr) {
87 LOGW("Failed to cancel timer ID %" PRIu32 ": not found", timerHandle);
88 } else if (timerRequest->instanceId != instanceId) {
89 LOGW("Failed to cancel timer ID %" PRIu32 ": permission denied",
90 timerHandle);
91 } else {
92 removeTimerRequestLocked(index);
93
94 if (index == 0) {
95 mSystemTimer.cancel();
96 handleExpiredTimersAndScheduleNextLocked();
97 }
98
99 success = true;
100 }
101
102 return success;
103 }
104
getTimerRequestByTimerHandleLocked(TimerHandle timerHandle,size_t * index)105 TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandleLocked(
106 TimerHandle timerHandle, size_t *index) {
107 for (size_t i = 0; i < mTimerRequests.size(); i++) {
108 if (mTimerRequests[i].timerHandle == timerHandle) {
109 if (index != nullptr) {
110 *index = i;
111 }
112 return &mTimerRequests[i];
113 }
114 }
115
116 return nullptr;
117 }
118
operator >(const TimerRequest & request) const119 bool TimerPool::TimerRequest::operator>(const TimerRequest& request) const {
120 return (expirationTime > request.expirationTime);
121 }
122
generateTimerHandleLocked()123 TimerHandle TimerPool::generateTimerHandleLocked() {
124 TimerHandle timerHandle;
125 if (mGenerateTimerHandleMustCheckUniqueness) {
126 timerHandle = generateUniqueTimerHandleLocked();
127 } else {
128 timerHandle = mLastTimerHandle + 1;
129 if (timerHandle == CHRE_TIMER_INVALID) {
130 // TODO: Consider that uniqueness checking can be reset when the number of
131 // timer requests reaches zero.
132 mGenerateTimerHandleMustCheckUniqueness = true;
133 timerHandle = generateUniqueTimerHandleLocked();
134 }
135 }
136
137 mLastTimerHandle = timerHandle;
138 return timerHandle;
139 }
140
generateUniqueTimerHandleLocked()141 TimerHandle TimerPool::generateUniqueTimerHandleLocked() {
142 TimerHandle timerHandle = mLastTimerHandle;
143 while (1) {
144 timerHandle++;
145 if (timerHandle != CHRE_TIMER_INVALID) {
146 TimerRequest *timerRequest =
147 getTimerRequestByTimerHandleLocked(timerHandle);
148 if (timerRequest == nullptr) {
149 return timerHandle;
150 }
151 }
152 }
153 }
154
isNewTimerAllowedLocked(bool isNanoappTimer) const155 bool TimerPool::isNewTimerAllowedLocked(bool isNanoappTimer) const {
156 static_assert(kMaxNanoappTimers <= kMaxTimerRequests,
157 "Max number of nanoapp timers is too big");
158 static_assert(kNumReservedNanoappTimers <= kMaxTimerRequests,
159 "Number of reserved nanoapp timers is too big");
160
161 bool allowed;
162 if (isNanoappTimer) {
163 allowed = (mNumNanoappTimers < kMaxNanoappTimers);
164 } else { // System timer
165 // We must not allow more system timers than the required amount of reserved
166 // timers for nanoapps.
167 constexpr size_t kMaxSystemTimers =
168 kMaxTimerRequests - kNumReservedNanoappTimers;
169 size_t numSystemTimers = mTimerRequests.size() - mNumNanoappTimers;
170 allowed = (numSystemTimers < kMaxSystemTimers);
171 }
172
173 return allowed;
174 }
175
insertTimerRequestLocked(const TimerRequest & timerRequest)176 bool TimerPool::insertTimerRequestLocked(const TimerRequest& timerRequest) {
177 bool isNanoappTimer = (timerRequest.instanceId != kSystemInstanceId);
178 bool success = isNewTimerAllowedLocked(isNanoappTimer) &&
179 mTimerRequests.push(timerRequest);
180
181 if (!success) {
182 LOG_OOM();
183 } else if (isNanoappTimer) {
184 mNumNanoappTimers++;
185 }
186
187 return success;
188 }
189
popTimerRequestLocked()190 void TimerPool::popTimerRequestLocked() {
191 CHRE_ASSERT(!mTimerRequests.empty());
192 if (!mTimerRequests.empty()) {
193 bool isNanoappTimer =
194 (mTimerRequests.top().instanceId != kSystemInstanceId);
195 mTimerRequests.pop();
196 if (isNanoappTimer) {
197 mNumNanoappTimers--;
198 }
199 }
200 }
201
removeTimerRequestLocked(size_t index)202 void TimerPool::removeTimerRequestLocked(size_t index) {
203 CHRE_ASSERT(index < mTimerRequests.size());
204 if (index < mTimerRequests.size()) {
205 bool isNanoappTimer =
206 (mTimerRequests[index].instanceId != kSystemInstanceId);
207 mTimerRequests.remove(index);
208 if (isNanoappTimer) {
209 mNumNanoappTimers--;
210 }
211 }
212 }
213
handleExpiredTimersAndScheduleNext()214 bool TimerPool::handleExpiredTimersAndScheduleNext() {
215 LockGuard<Mutex> lock(mMutex);
216 return handleExpiredTimersAndScheduleNextLocked();
217 }
218
handleExpiredTimersAndScheduleNextLocked()219 bool TimerPool::handleExpiredTimersAndScheduleNextLocked() {
220 bool success = false;
221 while (!mTimerRequests.empty()) {
222 Nanoseconds currentTime = SystemTime::getMonotonicTime();
223 TimerRequest& currentTimerRequest = mTimerRequests.top();
224 if (currentTime >= currentTimerRequest.expirationTime) {
225 // Post an event for an expired timer.
226 success = EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
227 currentTimerRequest.eventType,
228 const_cast<void *>(currentTimerRequest.cookie),
229 currentTimerRequest.callback, currentTimerRequest.instanceId);
230
231 // Reschedule the timer if needed, and release the current request.
232 if (!currentTimerRequest.isOneShot) {
233 // Important: we need to make a copy of currentTimerRequest here,
234 // because it's a reference to memory that may get moved during the
235 // insert operation (thereby invalidating it).
236 TimerRequest cyclicTimerRequest = currentTimerRequest;
237 cyclicTimerRequest.expirationTime = currentTime
238 + currentTimerRequest.duration;
239 popTimerRequestLocked();
240 CHRE_ASSERT(insertTimerRequestLocked(cyclicTimerRequest));
241 } else {
242 popTimerRequestLocked();
243 }
244 } else {
245 Nanoseconds duration = currentTimerRequest.expirationTime - currentTime;
246 mSystemTimer.set(handleSystemTimerCallback, this, duration);
247
248 // Assign success to true here to handle timers that tick before their
249 // expiration time. This should be rarely required, but for systems where
250 // a timer may tick earlier than requested the request is rescheduled with
251 // the remaining time as computed above.
252 success = true;
253 break;
254 }
255 }
256
257 return success;
258 }
259
handleSystemTimerCallback(void * timerPoolPtr)260 void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) {
261 auto callback = [](uint16_t /* eventType */, void *eventData) {
262 auto *timerPool = static_cast<TimerPool *>(eventData);
263 if (!timerPool->handleExpiredTimersAndScheduleNext()) {
264 LOGE("Timer callback invoked with no outstanding timers");
265 }
266 };
267
268 EventLoopManagerSingleton::get()->deferCallback(
269 SystemCallbackType::TimerPoolTick, timerPoolPtr, callback);
270 }
271
272 } // namespace chre
273