You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
7.1 KiB
221 lines
7.1 KiB
/* |
|
* 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<Mutex> lock(mMutex); |
|
|
|
TimerRequest timerRequest; |
|
timerRequest.nanoappInstanceId = nanoapp->getInstanceId(); |
|
timerRequest.timerHandle = generateTimerHandle(); |
|
timerRequest.expirationTime = SystemTime::getMonotonicTime() + duration; |
|
timerRequest.duration = duration; |
|
timerRequest.isOneShot = isOneShot; |
|
timerRequest.cookie = cookie; |
|
|
|
bool newTimerExpiresEarliest = |
|
(!mTimerRequests.empty() && mTimerRequests.top() > timerRequest); |
|
bool success = insertTimerRequest(timerRequest); |
|
|
|
if (success) { |
|
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 success ? timerRequest.timerHandle : CHRE_TIMER_INVALID; |
|
} |
|
|
|
bool TimerPool::cancelTimer(const Nanoapp *nanoapp, TimerHandle timerHandle) { |
|
CHRE_ASSERT(nanoapp); |
|
LockGuard<Mutex> 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->nanoappInstanceId != nanoapp->getInstanceId()) { |
|
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() { |
|
TimerHandle timerHandle = mLastTimerHandle; |
|
while (1) { |
|
timerHandle++; |
|
if (timerHandle != CHRE_TIMER_INVALID) { |
|
TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle); |
|
if (timerRequest == nullptr) { |
|
return timerHandle; |
|
} |
|
} |
|
} |
|
} |
|
|
|
bool TimerPool::insertTimerRequest(const TimerRequest& timerRequest) { |
|
// If the timer request was not inserted, simply append it to the list. |
|
bool success = (mTimerRequests.size() < kMaxTimerRequests) && |
|
mTimerRequests.push(timerRequest); |
|
if (!success) { |
|
LOGE("Failed to insert a timer request: out of memory"); |
|
} |
|
|
|
return success; |
|
} |
|
|
|
void TimerPool::onSystemTimerCallback() { |
|
// Gain exclusive access to the timer pool. This is needed because the context |
|
// of this callback is not defined. |
|
LockGuard<Mutex> lock(mMutex); |
|
if (!handleExpiredTimersAndScheduleNext()) { |
|
LOGE("Timer callback invoked with no outstanding timers"); |
|
} |
|
} |
|
|
|
bool TimerPool::handleExpiredTimersAndScheduleNext() { |
|
bool success = false; |
|
while (!mTimerRequests.empty()) { |
|
Nanoseconds currentTime = SystemTime::getMonotonicTime(); |
|
TimerRequest& currentTimerRequest = mTimerRequests.top(); |
|
if (currentTime >= currentTimerRequest.expirationTime) { |
|
// Post an event for an expired timer. |
|
success = mEventLoop.postEvent(CHRE_EVENT_TIMER, |
|
const_cast<void *>(currentTimerRequest.cookie), nullptr, |
|
kSystemInstanceId, |
|
currentTimerRequest.nanoappInstanceId); |
|
if (!success) { |
|
FATAL_ERROR("Failed to post timer event"); |
|
} |
|
|
|
// Reschedule the timer if needed, and release the current request. |
|
if (!currentTimerRequest.isOneShot) { |
|
// Important: we need to make a copy of currentTimerRequest here, |
|
// because it's a reference to memory that may get moved during the |
|
// insert operation (thereby invalidating it). |
|
TimerRequest cyclicTimerRequest = currentTimerRequest; |
|
cyclicTimerRequest.expirationTime = currentTime |
|
+ currentTimerRequest.duration; |
|
mTimerRequests.pop(); |
|
CHRE_ASSERT(insertTimerRequest(cyclicTimerRequest)); |
|
} else { |
|
mTimerRequests.pop(); |
|
} |
|
} else { |
|
Nanoseconds duration = currentTimerRequest.expirationTime - currentTime; |
|
mSystemTimer.set(handleSystemTimerCallback, this, duration); |
|
|
|
// Assign success to true here to handle timers that tick before their |
|
// expiration time. This should be rarely required, but for systems where |
|
// a timer may tick earlier than requested the request is rescheduled with |
|
// the remaining time as computed above. |
|
success = true; |
|
break; |
|
} |
|
} |
|
|
|
return success; |
|
} |
|
|
|
void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) { |
|
// Cast the context pointer to a TimerPool context and call into the callback |
|
// handler. |
|
TimerPool *timerPool = static_cast<TimerPool *>(timerPoolPtr); |
|
timerPool->onSystemTimerCallback(); |
|
} |
|
|
|
} // namespace chre
|
|
|