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 23 namespace chre { 24 25 TimerPool::TimerPool() { 26 if (!mSystemTimer.init()) { 27 FATAL_ERROR("Failed to initialize a system timer for the TimerPool"); 28 } 29 } 30 31 TimerHandle TimerPool::setTimer(const Nanoapp *nanoapp, Nanoseconds duration, 32 const void *cookie, bool isOneShot) { 33 CHRE_ASSERT(nanoapp); 34 35 TimerRequest timerRequest; 36 timerRequest.nanoappInstanceId = nanoapp->getInstanceId(); 37 timerRequest.timerHandle = generateTimerHandle(); 38 timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration; 39 timerRequest.duration = duration; 40 timerRequest.isOneShot = isOneShot; 41 timerRequest.cookie = cookie; 42 43 bool newTimerExpiresEarliest = 44 (!mTimerRequests.empty() && mTimerRequests.top() > timerRequest); 45 bool success = insertTimerRequest(timerRequest); 46 47 if (success) { 48 if (newTimerExpiresEarliest) { 49 if (mSystemTimer.isActive()) { 50 mSystemTimer.cancel(); 51 } 52 53 mSystemTimer.set(handleSystemTimerCallback, this, duration); 54 } else if (mTimerRequests.size() == 1) { 55 // If this timer request was the first, schedule it. 56 handleExpiredTimersAndScheduleNext(); 57 } 58 } 59 60 return success ? timerRequest.timerHandle : CHRE_TIMER_INVALID; 61 } 62 63 bool TimerPool::cancelTimer(const Nanoapp *nanoapp, TimerHandle timerHandle) { 64 CHRE_ASSERT(nanoapp); 65 66 size_t index; 67 bool success = false; 68 TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle, 69 &index); 70 71 if (timerRequest == nullptr) { 72 LOGW("Failed to cancel timer ID %" PRIu32 ": not found", timerHandle); 73 } else if (timerRequest->nanoappInstanceId != nanoapp->getInstanceId()) { 74 LOGW("Failed to cancel timer ID %" PRIu32 ": permission denied", 75 timerHandle); 76 } else { 77 mTimerRequests.remove(index); 78 if (index == 0) { 79 if (mSystemTimer.isActive()) { 80 mSystemTimer.cancel(); 81 } 82 83 handleExpiredTimersAndScheduleNext(); 84 } 85 86 success = true; 87 } 88 89 return success; 90 } 91 92 TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandle( 93 TimerHandle timerHandle, size_t *index) { 94 for (size_t i = 0; i < mTimerRequests.size(); i++) { 95 if (mTimerRequests[i].timerHandle == timerHandle) { 96 if (index != nullptr) { 97 *index = i; 98 } 99 return &mTimerRequests[i]; 100 } 101 } 102 103 return nullptr; 104 } 105 106 bool TimerPool::TimerRequest::operator>(const TimerRequest& request) const { 107 return (expirationTime > request.expirationTime); 108 } 109 110 TimerHandle TimerPool::generateTimerHandle() { 111 TimerHandle timerHandle; 112 if (mGenerateTimerHandleMustCheckUniqueness) { 113 timerHandle = generateUniqueTimerHandle(); 114 } else { 115 timerHandle = mLastTimerHandle + 1; 116 if (timerHandle == CHRE_TIMER_INVALID) { 117 // TODO: Consider that uniqueness checking can be reset when the number of 118 // timer requests reaches zero. 119 mGenerateTimerHandleMustCheckUniqueness = true; 120 timerHandle = generateUniqueTimerHandle(); 121 } 122 } 123 124 mLastTimerHandle = timerHandle; 125 return timerHandle; 126 } 127 128 TimerHandle TimerPool::generateUniqueTimerHandle() { 129 TimerHandle timerHandle = mLastTimerHandle; 130 while (1) { 131 timerHandle++; 132 if (timerHandle != CHRE_TIMER_INVALID) { 133 TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle); 134 if (timerRequest == nullptr) { 135 return timerHandle; 136 } 137 } 138 } 139 } 140 141 bool TimerPool::insertTimerRequest(const TimerRequest& timerRequest) { 142 // If the timer request was not inserted, simply append it to the list. 143 bool success = (mTimerRequests.size() < kMaxTimerRequests) && 144 mTimerRequests.push(timerRequest); 145 if (!success) { 146 LOGE("Failed to insert a timer request: out of memory"); 147 } 148 149 return success; 150 } 151 152 bool TimerPool::handleExpiredTimersAndScheduleNext() { 153 bool success = false; 154 while (!mTimerRequests.empty()) { 155 Nanoseconds currentTime = SystemTime::getMonotonicTime(); 156 TimerRequest& currentTimerRequest = mTimerRequests.top(); 157 if (currentTime >= currentTimerRequest.expirationTime) { 158 // Post an event for an expired timer. 159 success = EventLoopManagerSingleton::get()->getEventLoop().postEvent( 160 CHRE_EVENT_TIMER, const_cast<void *>(currentTimerRequest.cookie), 161 nullptr, kSystemInstanceId, currentTimerRequest.nanoappInstanceId); 162 163 // Reschedule the timer if needed, and release the current request. 164 if (!currentTimerRequest.isOneShot) { 165 // Important: we need to make a copy of currentTimerRequest here, 166 // because it's a reference to memory that may get moved during the 167 // insert operation (thereby invalidating it). 168 TimerRequest cyclicTimerRequest = currentTimerRequest; 169 cyclicTimerRequest.expirationTime = currentTime 170 + currentTimerRequest.duration; 171 mTimerRequests.pop(); 172 CHRE_ASSERT(insertTimerRequest(cyclicTimerRequest)); 173 } else { 174 mTimerRequests.pop(); 175 } 176 } else { 177 Nanoseconds duration = currentTimerRequest.expirationTime - currentTime; 178 mSystemTimer.set(handleSystemTimerCallback, this, duration); 179 180 // Assign success to true here to handle timers that tick before their 181 // expiration time. This should be rarely required, but for systems where 182 // a timer may tick earlier than requested the request is rescheduled with 183 // the remaining time as computed above. 184 success = true; 185 break; 186 } 187 } 188 189 return success; 190 } 191 192 void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) { 193 auto callback = [](uint16_t /* eventType */, void *eventData) { 194 auto *timerPool = static_cast<TimerPool *>(eventData); 195 if (!timerPool->handleExpiredTimersAndScheduleNext()) { 196 LOGE("Timer callback invoked with no outstanding timers"); 197 } 198 }; 199 200 EventLoopManagerSingleton::get()->deferCallback( 201 SystemCallbackType::TimerPoolTick, timerPoolPtr, callback); 202 } 203 204 } // namespace chre 205