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