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