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/timer_pool.h"
18 #include "chre/core/event.h"
19 #include "chre/core/event_loop.h"
20 #include "chre/core/event_loop_common.h"
21 #include "chre/core/event_loop_manager.h"
22 #include "chre/platform/fatal_error.h"
23 #include "chre/platform/system_time.h"
24 #include "chre/target_platform/log.h"
25 #include "chre/util/lock_guard.h"
26 #include "chre/util/nested_data_ptr.h"
27
28 namespace chre {
29
TimerPool()30 TimerPool::TimerPool() {
31 if (!mSystemTimer.init()) {
32 FATAL_ERROR("Failed to initialize a system timer for the TimerPool");
33 }
34 }
35
setSystemTimer(Nanoseconds duration,SystemEventCallbackFunction * callback,SystemCallbackType callbackType,void * data)36 TimerHandle TimerPool::setSystemTimer(Nanoseconds duration,
37 SystemEventCallbackFunction *callback,
38 SystemCallbackType callbackType,
39 void *data) {
40 CHRE_ASSERT(callback != nullptr);
41 TimerHandle timerHandle =
42 setTimer(kSystemInstanceId, duration, data, callback, callbackType,
43 true /* isOneShot */);
44
45 if (timerHandle == CHRE_TIMER_INVALID) {
46 FATAL_ERROR("Failed to set system timer");
47 }
48
49 return timerHandle;
50 }
51
cancelAllNanoappTimers(const Nanoapp * nanoapp)52 uint32_t TimerPool::cancelAllNanoappTimers(const Nanoapp *nanoapp) {
53 CHRE_ASSERT(nanoapp != nullptr);
54 LockGuard<Mutex> lock(mMutex);
55
56 uint32_t numTimersCancelled = 0;
57
58 // Iterate backward as we remove requests from the list.
59 for (int i = static_cast<int>(mTimerRequests.size()) - 1; i >= 0; i--) {
60 size_t iAsSize = static_cast<size_t>(i);
61 const TimerRequest &request = mTimerRequests[iAsSize];
62 if (request.instanceId == nanoapp->getInstanceId()) {
63 numTimersCancelled++;
64 removeTimerRequestLocked(iAsSize);
65 }
66 }
67
68 return numTimersCancelled;
69 }
70
setTimer(uint16_t instanceId,Nanoseconds duration,const void * cookie,SystemEventCallbackFunction * systemCallback,SystemCallbackType callbackType,bool isOneShot)71 TimerHandle TimerPool::setTimer(uint16_t instanceId, Nanoseconds duration,
72 const void *cookie,
73 SystemEventCallbackFunction *systemCallback,
74 SystemCallbackType callbackType,
75 bool isOneShot) {
76 LockGuard<Mutex> lock(mMutex);
77
78 TimerRequest timerRequest;
79 timerRequest.instanceId = instanceId;
80 timerRequest.timerHandle = generateTimerHandleLocked();
81 timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration;
82 timerRequest.duration = duration;
83 timerRequest.cookie = cookie;
84 timerRequest.systemCallback = systemCallback;
85 timerRequest.callbackType = callbackType;
86 timerRequest.isOneShot = isOneShot;
87
88 bool success = insertTimerRequestLocked(timerRequest);
89
90 if (success) {
91 if (mTimerRequests.size() == 1) {
92 // If this timer request was the first, schedule it.
93 handleExpiredTimersAndScheduleNextLocked();
94 } else {
95 // If there was already a timer pending before this, and we just inserted
96 // to the top of the queue, just update the system timer. This is slightly
97 // more efficient than calling into
98 // handleExpiredTimersAndScheduleNextLocked().
99 bool newRequestExpiresFirst =
100 timerRequest.timerHandle == mTimerRequests.top().timerHandle;
101 if (newRequestExpiresFirst) {
102 mSystemTimer.set(handleSystemTimerCallback, this, duration);
103 }
104 }
105 }
106
107 return success ? timerRequest.timerHandle : CHRE_TIMER_INVALID;
108 }
109
cancelTimer(uint16_t instanceId,TimerHandle timerHandle)110 bool TimerPool::cancelTimer(uint16_t instanceId, TimerHandle timerHandle) {
111 LockGuard<Mutex> lock(mMutex);
112 size_t index;
113 bool success = false;
114 TimerRequest *timerRequest =
115 getTimerRequestByTimerHandleLocked(timerHandle, &index);
116
117 if (timerRequest == nullptr) {
118 LOGW("Failed to cancel timer ID %" PRIu32 ": not found", timerHandle);
119 } else if (timerRequest->instanceId != instanceId) {
120 LOGW("Failed to cancel timer ID %" PRIu32 ": permission denied",
121 timerHandle);
122 } else {
123 removeTimerRequestLocked(index);
124 success = true;
125 }
126
127 return success;
128 }
129
getTimerRequestByTimerHandleLocked(TimerHandle timerHandle,size_t * index)130 TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandleLocked(
131 TimerHandle timerHandle, size_t *index) {
132 for (size_t i = 0; i < mTimerRequests.size(); ++i) {
133 if (mTimerRequests[i].timerHandle == timerHandle) {
134 if (index != nullptr) {
135 *index = i;
136 }
137 return &mTimerRequests[i];
138 }
139 }
140
141 return nullptr;
142 }
143
operator >(const TimerRequest & request) const144 bool TimerPool::TimerRequest::operator>(const TimerRequest &request) const {
145 return expirationTime > request.expirationTime;
146 }
147
generateTimerHandleLocked()148 TimerHandle TimerPool::generateTimerHandleLocked() {
149 TimerHandle timerHandle;
150 if (mGenerateTimerHandleMustCheckUniqueness) {
151 timerHandle = generateUniqueTimerHandleLocked();
152 } else {
153 timerHandle = mLastTimerHandle + 1;
154 if (timerHandle == CHRE_TIMER_INVALID) {
155 // TODO: Consider that uniqueness checking can be reset when the number
156 // of timer requests reaches zero.
157 mGenerateTimerHandleMustCheckUniqueness = true;
158 timerHandle = generateUniqueTimerHandleLocked();
159 }
160 }
161
162 mLastTimerHandle = timerHandle;
163 return timerHandle;
164 }
165
generateUniqueTimerHandleLocked()166 TimerHandle TimerPool::generateUniqueTimerHandleLocked() {
167 TimerHandle timerHandle = mLastTimerHandle;
168 while (1) {
169 timerHandle++;
170 if (timerHandle != CHRE_TIMER_INVALID) {
171 TimerRequest *timerRequest =
172 getTimerRequestByTimerHandleLocked(timerHandle);
173 if (timerRequest == nullptr) {
174 return timerHandle;
175 }
176 }
177 }
178 }
179
isNewTimerAllowedLocked(bool isNanoappTimer) const180 bool TimerPool::isNewTimerAllowedLocked(bool isNanoappTimer) const {
181 static_assert(kMaxNanoappTimers <= kMaxTimerRequests,
182 "Max number of nanoapp timers is too big");
183 static_assert(kNumReservedNanoappTimers <= kMaxTimerRequests,
184 "Number of reserved nanoapp timers is too big");
185
186 bool allowed;
187 if (isNanoappTimer) {
188 allowed = (mNumNanoappTimers < kMaxNanoappTimers);
189 } else { // System timer
190 // We must not allow more system timers than the required amount of
191 // reserved timers for nanoapps.
192 constexpr size_t kMaxSystemTimers =
193 kMaxTimerRequests - kNumReservedNanoappTimers;
194 size_t numSystemTimers = mTimerRequests.size() - mNumNanoappTimers;
195 allowed = (numSystemTimers < kMaxSystemTimers);
196 }
197
198 return allowed;
199 }
200
insertTimerRequestLocked(const TimerRequest & timerRequest)201 bool TimerPool::insertTimerRequestLocked(const TimerRequest &timerRequest) {
202 bool isNanoappTimer = (timerRequest.instanceId != kSystemInstanceId);
203 bool success = isNewTimerAllowedLocked(isNanoappTimer) &&
204 mTimerRequests.push(timerRequest);
205
206 if (!success) {
207 LOG_OOM();
208 } else if (isNanoappTimer) {
209 mNumNanoappTimers++;
210 }
211
212 return success;
213 }
214
popTimerRequestLocked()215 void TimerPool::popTimerRequestLocked() {
216 CHRE_ASSERT(!mTimerRequests.empty());
217 if (!mTimerRequests.empty()) {
218 bool isNanoappTimer =
219 (mTimerRequests.top().instanceId != kSystemInstanceId);
220 mTimerRequests.pop();
221 if (isNanoappTimer) {
222 mNumNanoappTimers--;
223 }
224 }
225 }
226
removeTimerRequestLocked(size_t index)227 void TimerPool::removeTimerRequestLocked(size_t index) {
228 CHRE_ASSERT(index < mTimerRequests.size());
229 if (index < mTimerRequests.size()) {
230 bool isNanoappTimer =
231 (mTimerRequests[index].instanceId != kSystemInstanceId);
232 mTimerRequests.remove(index);
233 if (isNanoappTimer) {
234 mNumNanoappTimers--;
235 }
236
237 if (index == 0) {
238 mSystemTimer.cancel();
239 handleExpiredTimersAndScheduleNextLocked();
240 }
241 }
242 }
243
handleExpiredTimersAndScheduleNext()244 bool TimerPool::handleExpiredTimersAndScheduleNext() {
245 LockGuard<Mutex> lock(mMutex);
246 return handleExpiredTimersAndScheduleNextLocked();
247 }
248
handleExpiredTimersAndScheduleNextLocked()249 bool TimerPool::handleExpiredTimersAndScheduleNextLocked() {
250 bool handledExpiredTimer = false;
251
252 while (!mTimerRequests.empty()) {
253 Nanoseconds currentTime = SystemTime::getMonotonicTime();
254 TimerRequest ¤tTimerRequest = mTimerRequests.top();
255 if (currentTime >= currentTimerRequest.expirationTime) {
256 handledExpiredTimer = true;
257
258 // This timer has expired, so post an event if it is a nanoapp timer, or
259 // submit a deferred callback if it's a system timer.
260 bool success;
261 if (currentTimerRequest.instanceId == kSystemInstanceId) {
262 success = EventLoopManagerSingleton::get()->deferCallback(
263 currentTimerRequest.callbackType,
264 const_cast<void *>(currentTimerRequest.cookie),
265 currentTimerRequest.systemCallback);
266 } else {
267 success = EventLoopManagerSingleton::get()->deferCallback(
268 SystemCallbackType::TimerPoolTimerExpired,
269 NestedDataPtr<TimerHandle>(currentTimerRequest.timerHandle),
270 TimerPool::handleTimerExpiredCallback,
271 this);
272 }
273 if (!success) {
274 LOGW("Failed to defer timer callback");
275 }
276
277 rescheduleAndRemoveExpiredTimersLocked(currentTimerRequest);
278 } else {
279 // Update the system timer to reflect the duration until the closest
280 // expiry (mTimerRequests is sorted by expiry, so we just do this for
281 // the first timer found which has not expired yet)
282 Nanoseconds duration = currentTimerRequest.expirationTime - currentTime;
283 mSystemTimer.set(handleSystemTimerCallback, this, duration);
284 break;
285 }
286 }
287
288 return handledExpiredTimer;
289 }
290
rescheduleAndRemoveExpiredTimersLocked(const TimerRequest & request)291 void TimerPool::rescheduleAndRemoveExpiredTimersLocked(
292 const TimerRequest &request) {
293 if (request.isOneShot && request.instanceId == kSystemInstanceId) {
294 popTimerRequestLocked();
295 } else {
296 TimerRequest copyRequest = request;
297 copyRequest.expirationTime = request.isOneShot
298 ? Nanoseconds(UINT64_MAX)
299 : request.expirationTime + request.duration;
300 popTimerRequestLocked();
301 CHRE_ASSERT(insertTimerRequestLocked(copyRequest));
302 }
303 }
304
hasNanoappTimers(uint16_t instanceId)305 bool TimerPool::hasNanoappTimers(uint16_t instanceId) {
306 LockGuard<Mutex> lock(mMutex);
307
308 for (size_t i = 0; i < mTimerRequests.size(); i++) {
309 const TimerRequest &request = mTimerRequests[i];
310 if (request.instanceId == instanceId) {
311 return true;
312 }
313 }
314 return false;
315 }
316
handleSystemTimerCallback(void * timerPoolPtr)317 void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) {
318 auto callback = [](uint16_t /* type */, void *data, void * /* extraData */) {
319 auto *timerPool = static_cast<TimerPool *>(data);
320 if (!timerPool->handleExpiredTimersAndScheduleNext()) {
321 // Means that the system timer invoked our callback before the next
322 // timer expired. Possible in rare race conditions with time removal,
323 // but could indicate a faulty SystemTimer implementation if this
324 // happens often. Not a major problem - we'll just reset the timer to
325 // the next expiration.
326 LOGW("Timer callback invoked prior to expiry");
327 }
328 };
329
330 EventLoopManagerSingleton::get()->deferCallback(
331 SystemCallbackType::TimerPoolTick, timerPoolPtr, callback);
332 }
333
handleTimerExpiredCallback(uint16_t,void * data,void * extraData)334 void TimerPool::handleTimerExpiredCallback(uint16_t /* type */, void *data,
335 void *extraData) {
336 NestedDataPtr<TimerHandle> timerHandle(data);
337 TimerPool* timerPool = static_cast<TimerPool*>(extraData);
338 size_t index;
339 TimerRequest currentTimerRequest;
340
341 {
342 LockGuard<Mutex> lock(timerPool->mMutex);
343 TimerRequest* timerRequest =
344 timerPool->getTimerRequestByTimerHandleLocked(
345 timerHandle, &index);
346 if (timerRequest == nullptr) {
347 return;
348 }
349
350 currentTimerRequest = *timerRequest;
351 if (currentTimerRequest.isOneShot) {
352 timerPool->removeTimerRequestLocked(index);
353 }
354 }
355
356 if (!EventLoopManagerSingleton::get()->getEventLoop()
357 .deliverEventSync(
358 currentTimerRequest.instanceId,
359 CHRE_EVENT_TIMER,
360 const_cast<void*>(currentTimerRequest.cookie))) {
361 LOGW("Failed to deliver timer event");
362 }
363 }
364
365 } // namespace chre
366