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