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