• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &currentTimerRequest = 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