xref: /aosp_15_r20/system/chre/core/timer_pool.cc (revision 84e339476a462649f82315436d70fd732297a399)
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 &currentTimerRequest = 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