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 &currentTimerRequest = 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