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/event_loop.h"
18 #include "chre/core/event_loop_manager.h"
19 #include "chre/core/timer_pool.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,SystemCallbackFunction * callback,SystemCallbackType callbackType,const void * cookie)32 TimerHandle TimerPool::setSystemTimer(
33     Nanoseconds duration, SystemCallbackFunction *callback,
34     SystemCallbackType callbackType, const void *cookie) {
35   TimerHandle timerHandle = setTimer(
36       kSystemInstanceId, duration, callback,
37       static_cast<uint16_t>(callbackType), cookie, true /* isOneShot */);
38 
39   if (timerHandle == CHRE_TIMER_INVALID) {
40     FATAL_ERROR("Failed to set system timer");
41   }
42 
43   return timerHandle;
44 }
45 
setTimer(uint32_t instanceId,Nanoseconds duration,SystemCallbackFunction * callback,uint16_t eventType,const void * cookie,bool isOneShot)46 TimerHandle TimerPool::setTimer(
47     uint32_t instanceId, Nanoseconds duration,
48     SystemCallbackFunction *callback, uint16_t eventType,
49     const void *cookie, bool isOneShot) {
50   LockGuard<Mutex> lock(mMutex);
51 
52   TimerRequest timerRequest;
53   timerRequest.instanceId = instanceId;
54   timerRequest.timerHandle = generateTimerHandleLocked();
55   timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration;
56   timerRequest.duration = duration;
57   timerRequest.isOneShot = isOneShot;
58   timerRequest.callback = callback;
59   timerRequest.eventType = eventType;
60   timerRequest.cookie = cookie;
61 
62   bool newTimerExpiresEarliest =
63       (!mTimerRequests.empty() && mTimerRequests.top() > timerRequest);
64   bool success = insertTimerRequestLocked(timerRequest);
65 
66   if (success) {
67     if (newTimerExpiresEarliest) {
68       mSystemTimer.set(handleSystemTimerCallback, this, duration);
69     } else if (mTimerRequests.size() == 1) {
70       // If this timer request was the first, schedule it.
71       handleExpiredTimersAndScheduleNextLocked();
72     }
73   }
74 
75   return success ? timerRequest.timerHandle : CHRE_TIMER_INVALID;
76 }
77 
cancelTimer(uint32_t instanceId,TimerHandle timerHandle)78 bool TimerPool::cancelTimer(
79     uint32_t instanceId, TimerHandle timerHandle) {
80   LockGuard<Mutex> lock(mMutex);
81   size_t index;
82   bool success = false;
83   TimerRequest *timerRequest = getTimerRequestByTimerHandleLocked(timerHandle,
84       &index);
85 
86   if (timerRequest == nullptr) {
87     LOGW("Failed to cancel timer ID %" PRIu32 ": not found", timerHandle);
88   } else if (timerRequest->instanceId != instanceId) {
89     LOGW("Failed to cancel timer ID %" PRIu32 ": permission denied",
90          timerHandle);
91   } else {
92     removeTimerRequestLocked(index);
93 
94     if (index == 0) {
95       mSystemTimer.cancel();
96       handleExpiredTimersAndScheduleNextLocked();
97     }
98 
99     success = true;
100   }
101 
102   return success;
103 }
104 
getTimerRequestByTimerHandleLocked(TimerHandle timerHandle,size_t * index)105 TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandleLocked(
106     TimerHandle timerHandle, size_t *index) {
107   for (size_t i = 0; i < mTimerRequests.size(); i++) {
108     if (mTimerRequests[i].timerHandle == timerHandle) {
109       if (index != nullptr) {
110         *index = i;
111       }
112       return &mTimerRequests[i];
113     }
114   }
115 
116   return nullptr;
117 }
118 
operator >(const TimerRequest & request) const119 bool TimerPool::TimerRequest::operator>(const TimerRequest& request) const {
120   return (expirationTime > request.expirationTime);
121 }
122 
generateTimerHandleLocked()123 TimerHandle TimerPool::generateTimerHandleLocked() {
124   TimerHandle timerHandle;
125   if (mGenerateTimerHandleMustCheckUniqueness) {
126     timerHandle = generateUniqueTimerHandleLocked();
127   } else {
128     timerHandle = mLastTimerHandle + 1;
129     if (timerHandle == CHRE_TIMER_INVALID) {
130       // TODO: Consider that uniqueness checking can be reset when the number of
131       // timer requests reaches zero.
132       mGenerateTimerHandleMustCheckUniqueness = true;
133       timerHandle = generateUniqueTimerHandleLocked();
134     }
135   }
136 
137   mLastTimerHandle = timerHandle;
138   return timerHandle;
139 }
140 
generateUniqueTimerHandleLocked()141 TimerHandle TimerPool::generateUniqueTimerHandleLocked() {
142   TimerHandle timerHandle = mLastTimerHandle;
143   while (1) {
144     timerHandle++;
145     if (timerHandle != CHRE_TIMER_INVALID) {
146       TimerRequest *timerRequest =
147           getTimerRequestByTimerHandleLocked(timerHandle);
148       if (timerRequest == nullptr) {
149         return timerHandle;
150       }
151     }
152   }
153 }
154 
isNewTimerAllowedLocked(bool isNanoappTimer) const155 bool TimerPool::isNewTimerAllowedLocked(bool isNanoappTimer) const {
156   static_assert(kMaxNanoappTimers <= kMaxTimerRequests,
157                 "Max number of nanoapp timers is too big");
158   static_assert(kNumReservedNanoappTimers <= kMaxTimerRequests,
159                 "Number of reserved nanoapp timers is too big");
160 
161   bool allowed;
162   if (isNanoappTimer) {
163     allowed = (mNumNanoappTimers < kMaxNanoappTimers);
164   } else { // System timer
165     // We must not allow more system timers than the required amount of reserved
166     // timers for nanoapps.
167     constexpr size_t kMaxSystemTimers =
168         kMaxTimerRequests - kNumReservedNanoappTimers;
169     size_t numSystemTimers = mTimerRequests.size() - mNumNanoappTimers;
170     allowed = (numSystemTimers < kMaxSystemTimers);
171   }
172 
173   return allowed;
174 }
175 
insertTimerRequestLocked(const TimerRequest & timerRequest)176 bool TimerPool::insertTimerRequestLocked(const TimerRequest& timerRequest) {
177   bool isNanoappTimer = (timerRequest.instanceId != kSystemInstanceId);
178   bool success = isNewTimerAllowedLocked(isNanoappTimer) &&
179       mTimerRequests.push(timerRequest);
180 
181   if (!success) {
182     LOG_OOM();
183   } else if (isNanoappTimer) {
184     mNumNanoappTimers++;
185   }
186 
187   return success;
188 }
189 
popTimerRequestLocked()190 void TimerPool::popTimerRequestLocked() {
191   CHRE_ASSERT(!mTimerRequests.empty());
192   if (!mTimerRequests.empty()) {
193     bool isNanoappTimer =
194         (mTimerRequests.top().instanceId != kSystemInstanceId);
195     mTimerRequests.pop();
196     if (isNanoappTimer) {
197       mNumNanoappTimers--;
198     }
199   }
200 }
201 
removeTimerRequestLocked(size_t index)202 void TimerPool::removeTimerRequestLocked(size_t index) {
203   CHRE_ASSERT(index < mTimerRequests.size());
204   if (index < mTimerRequests.size()) {
205     bool isNanoappTimer =
206         (mTimerRequests[index].instanceId != kSystemInstanceId);
207     mTimerRequests.remove(index);
208     if (isNanoappTimer) {
209       mNumNanoappTimers--;
210     }
211   }
212 }
213 
handleExpiredTimersAndScheduleNext()214 bool TimerPool::handleExpiredTimersAndScheduleNext() {
215   LockGuard<Mutex> lock(mMutex);
216   return handleExpiredTimersAndScheduleNextLocked();
217 }
218 
handleExpiredTimersAndScheduleNextLocked()219 bool TimerPool::handleExpiredTimersAndScheduleNextLocked() {
220   bool success = false;
221   while (!mTimerRequests.empty()) {
222     Nanoseconds currentTime = SystemTime::getMonotonicTime();
223     TimerRequest& currentTimerRequest = mTimerRequests.top();
224     if (currentTime >= currentTimerRequest.expirationTime) {
225       // Post an event for an expired timer.
226       success = EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
227           currentTimerRequest.eventType,
228           const_cast<void *>(currentTimerRequest.cookie),
229           currentTimerRequest.callback, currentTimerRequest.instanceId);
230 
231       // Reschedule the timer if needed, and release the current request.
232       if (!currentTimerRequest.isOneShot) {
233         // Important: we need to make a copy of currentTimerRequest here,
234         // because it's a reference to memory that may get moved during the
235         // insert operation (thereby invalidating it).
236         TimerRequest cyclicTimerRequest = currentTimerRequest;
237         cyclicTimerRequest.expirationTime = currentTime
238             + currentTimerRequest.duration;
239         popTimerRequestLocked();
240         CHRE_ASSERT(insertTimerRequestLocked(cyclicTimerRequest));
241       } else {
242         popTimerRequestLocked();
243       }
244     } else {
245       Nanoseconds duration = currentTimerRequest.expirationTime - currentTime;
246       mSystemTimer.set(handleSystemTimerCallback, this, duration);
247 
248       // Assign success to true here to handle timers that tick before their
249       // expiration time. This should be rarely required, but for systems where
250       // a timer may tick earlier than requested the request is rescheduled with
251       // the remaining time as computed above.
252       success = true;
253       break;
254     }
255   }
256 
257   return success;
258 }
259 
handleSystemTimerCallback(void * timerPoolPtr)260 void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) {
261   auto callback = [](uint16_t /* eventType */, void *eventData) {
262     auto *timerPool = static_cast<TimerPool *>(eventData);
263     if (!timerPool->handleExpiredTimersAndScheduleNext()) {
264       LOGE("Timer callback invoked with no outstanding timers");
265     }
266   };
267 
268   EventLoopManagerSingleton::get()->deferCallback(
269       SystemCallbackType::TimerPoolTick, timerPoolPtr, callback);
270 }
271 
272 }  // namespace chre
273