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