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 26 TimerPool::TimerPool() { 27 if (!mSystemTimer.init()) { 28 FATAL_ERROR("Failed to initialize a system timer for the TimerPool"); 29 } 30 } 31 32 TimerHandle TimerPool::setSystemTimer(Nanoseconds duration, 33 SystemCallbackFunction *callback, 34 SystemCallbackType callbackType, 35 const void *cookie) { 36 TimerHandle timerHandle = setTimer(kSystemInstanceId, duration, callback, 37 static_cast<uint16_t>(callbackType), 38 cookie, true /* isOneShot */); 39 40 if (timerHandle == CHRE_TIMER_INVALID) { 41 FATAL_ERROR("Failed to set system timer"); 42 } 43 44 return timerHandle; 45 } 46 47 TimerHandle TimerPool::setTimer(uint32_t instanceId, Nanoseconds duration, 48 SystemCallbackFunction *callback, 49 uint16_t eventType, const void *cookie, 50 bool isOneShot) { 51 LockGuard<Mutex> lock(mMutex); 52 53 TimerRequest timerRequest; 54 timerRequest.instanceId = instanceId; 55 timerRequest.timerHandle = generateTimerHandleLocked(); 56 timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration; 57 timerRequest.duration = duration; 58 timerRequest.isOneShot = isOneShot; 59 timerRequest.callback = callback; 60 timerRequest.eventType = eventType; 61 timerRequest.cookie = cookie; 62 63 bool newTimerExpiresEarliest = 64 (!mTimerRequests.empty() && mTimerRequests.top() > timerRequest); 65 bool success = insertTimerRequestLocked(timerRequest); 66 67 if (success) { 68 if (newTimerExpiresEarliest) { 69 mSystemTimer.set(handleSystemTimerCallback, this, duration); 70 } else if (mTimerRequests.size() == 1) { 71 // If this timer request was the first, schedule it. 72 handleExpiredTimersAndScheduleNextLocked(); 73 } 74 } 75 76 return success ? timerRequest.timerHandle : CHRE_TIMER_INVALID; 77 } 78 79 bool TimerPool::cancelTimer(uint32_t instanceId, TimerHandle timerHandle) { 80 LockGuard<Mutex> lock(mMutex); 81 size_t index; 82 bool success = false; 83 TimerRequest *timerRequest = 84 getTimerRequestByTimerHandleLocked(timerHandle, &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 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 119 bool TimerPool::TimerRequest::operator>(const TimerRequest &request) const { 120 return (expirationTime > request.expirationTime); 121 } 122 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 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 155 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 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 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 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 214 bool TimerPool::handleExpiredTimersAndScheduleNext() { 215 LockGuard<Mutex> lock(mMutex); 216 return handleExpiredTimersAndScheduleNextLocked(); 217 } 218 219 bool TimerPool::handleExpiredTimersAndScheduleNextLocked() { 220 bool success = false; 221 while (!mTimerRequests.empty()) { 222 Nanoseconds currentTime = SystemTime::getMonotonicTime(); 223 TimerRequest ¤tTimerRequest = 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 = 238 currentTime + 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 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