/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "chre/core/event_loop.h" #include "chre/core/timer_pool.h" #include "chre/platform/fatal_error.h" #include "chre/platform/system_time.h" #include "chre/util/lock_guard.h" namespace chre { TimerPool::TimerPool(EventLoop& eventLoop) : mEventLoop(eventLoop) { if (!mSystemTimer.init()) { FATAL_ERROR("Failed to initialize a system timer for the TimerPool"); } } TimerHandle TimerPool::setTimer(const Nanoapp *nanoapp, Nanoseconds duration, const void *cookie, bool isOneShot) { CHRE_ASSERT(nanoapp); LockGuard lock(mMutex); TimerRequest timerRequest; timerRequest.requestingNanoapp = nanoapp; timerRequest.timerHandle = generateTimerHandle(); timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration; timerRequest.duration = duration; timerRequest.isOneShot = isOneShot; timerRequest.cookie = cookie; bool newTimerExpiresEarliest = (!mTimerRequests.empty() && mTimerRequests.top() > timerRequest); insertTimerRequest(timerRequest); LOGD("App %" PRIx64 " requested timer with duration %" PRIu64 "ns", nanoapp->getAppId(), duration.toRawNanoseconds()); if (newTimerExpiresEarliest) { if (mSystemTimer.isActive()) { mSystemTimer.cancel(); } mSystemTimer.set(handleSystemTimerCallback, this, duration); } else if (mTimerRequests.size() == 1) { // If this timer request was the first, schedule it. handleExpiredTimersAndScheduleNext(); } return timerRequest.timerHandle; } bool TimerPool::cancelTimer(const Nanoapp *nanoapp, TimerHandle timerHandle) { CHRE_ASSERT(nanoapp); LockGuard lock(mMutex); size_t index; bool success = false; TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle, &index); if (timerRequest == nullptr) { LOGW("Failed to cancel timer ID %" PRIu32 ": not found", timerHandle); } else if (timerRequest->requestingNanoapp != nanoapp) { LOGW("Failed to cancel timer ID %" PRIu32 ": permission denied", timerHandle); } else { TimerHandle cancelledTimerHandle = timerRequest->timerHandle; mTimerRequests.remove(index); if (index == 0) { if (mSystemTimer.isActive()) { mSystemTimer.cancel(); } handleExpiredTimersAndScheduleNext(); } LOGD("App %" PRIx64 " cancelled timer %" PRIu32, nanoapp->getAppId(), cancelledTimerHandle); success = true; } return success; } TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandle( TimerHandle timerHandle, size_t *index) { for (size_t i = 0; i < mTimerRequests.size(); i++) { if (mTimerRequests[i].timerHandle == timerHandle) { if (index != nullptr) { *index = i; } return &mTimerRequests[i]; } } return nullptr; } bool TimerPool::TimerRequest::operator>(const TimerRequest& request) const { return (expirationTime > request.expirationTime); } TimerHandle TimerPool::generateTimerHandle() { TimerHandle timerHandle; if (mGenerateTimerHandleMustCheckUniqueness) { timerHandle = generateUniqueTimerHandle(); } else { timerHandle = mLastTimerHandle + 1; if (timerHandle == CHRE_TIMER_INVALID) { // TODO: Consider that uniqueness checking can be reset when the number of // timer requests reaches zero. mGenerateTimerHandleMustCheckUniqueness = true; timerHandle = generateUniqueTimerHandle(); } } mLastTimerHandle = timerHandle; return timerHandle; } TimerHandle TimerPool::generateUniqueTimerHandle() { size_t timerHandle = mLastTimerHandle; while (1) { timerHandle++; if (timerHandle != CHRE_TIMER_INVALID) { TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle); if (timerRequest == nullptr) { return timerHandle; } } } } void TimerPool::insertTimerRequest(const TimerRequest& timerRequest) { // If the timer request was not inserted, simply append it to the list. if (!mTimerRequests.push(timerRequest)) { FATAL_ERROR("Failed to insert a timer request: out of memory"); } } void TimerPool::onSystemTimerCallback() { // Gain exclusive access to the timer pool. This is needed because the context // of this callback is not defined. LockGuard lock(mMutex); if (!handleExpiredTimersAndScheduleNext()) { LOGE("Timer callback invoked with no outstanding timers"); } } bool TimerPool::handleExpiredTimersAndScheduleNext() { bool eventWasPosted = false; while (!mTimerRequests.empty()) { Nanoseconds currentTime = SystemTime::getMonotonicTime(); TimerRequest& currentTimerRequest = mTimerRequests.top(); if (currentTime >= currentTimerRequest.expirationTime) { // Post an event for an expired timer. mEventLoop.postEvent(CHRE_EVENT_TIMER, const_cast(currentTimerRequest.cookie), nullptr, kSystemInstanceId, currentTimerRequest.requestingNanoapp->getInstanceId()); eventWasPosted = true; // Reschedule the timer if needed. if (!currentTimerRequest.isOneShot) { currentTimerRequest.expirationTime = currentTime + currentTimerRequest.duration; insertTimerRequest(currentTimerRequest); } // Release the current request. mTimerRequests.pop(); } else { Nanoseconds duration = currentTimerRequest.expirationTime - currentTime; mSystemTimer.set(handleSystemTimerCallback, this, duration); break; } } return eventWasPosted; } void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) { // Cast the context pointer to a TimerPool context and call into the callback // handler. TimerPool *timerPool = static_cast(timerPoolPtr); timerPool->onSystemTimerCallback(); } } // namespace chre