Home | History | Annotate | Download | only in core
      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