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.requestingNanoapp = nanoapp;
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   insertTimerRequest(timerRequest);
48 
49   LOGD("App %" PRIx64 " requested timer with duration %" PRIu64 "ns",
50       nanoapp->getAppId(), duration.toRawNanoseconds());
51 
52   if (newTimerExpiresEarliest) {
53     if (mSystemTimer.isActive()) {
54       mSystemTimer.cancel();
55     }
56 
57     mSystemTimer.set(handleSystemTimerCallback, this, duration);
58   } else if (mTimerRequests.size() == 1) {
59     // If this timer request was the first, schedule it.
60     handleExpiredTimersAndScheduleNext();
61   }
62 
63   return timerRequest.timerHandle;
64 }
65 
cancelTimer(const Nanoapp * nanoapp,TimerHandle timerHandle)66 bool TimerPool::cancelTimer(const Nanoapp *nanoapp, TimerHandle timerHandle) {
67   CHRE_ASSERT(nanoapp);
68   LockGuard<Mutex> lock(mMutex);
69 
70   size_t index;
71   bool success = false;
72   TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle,
73       &index);
74 
75   if (timerRequest == nullptr) {
76     LOGW("Failed to cancel timer ID %" PRIu32 ": not found", timerHandle);
77   } else if (timerRequest->requestingNanoapp != nanoapp) {
78     LOGW("Failed to cancel timer ID %" PRIu32 ": permission denied",
79          timerHandle);
80   } else {
81     TimerHandle cancelledTimerHandle = timerRequest->timerHandle;
82     mTimerRequests.remove(index);
83     if (index == 0) {
84       if (mSystemTimer.isActive()) {
85         mSystemTimer.cancel();
86       }
87 
88       handleExpiredTimersAndScheduleNext();
89     }
90 
91     LOGD("App %" PRIx64 " cancelled timer %" PRIu32, nanoapp->getAppId(),
92          cancelledTimerHandle);
93     success = true;
94   }
95 
96   return success;
97 }
98 
getTimerRequestByTimerHandle(TimerHandle timerHandle,size_t * index)99 TimerPool::TimerRequest *TimerPool::getTimerRequestByTimerHandle(
100     TimerHandle timerHandle, size_t *index) {
101   for (size_t i = 0; i < mTimerRequests.size(); i++) {
102     if (mTimerRequests[i].timerHandle == timerHandle) {
103       if (index != nullptr) {
104         *index = i;
105       }
106       return &mTimerRequests[i];
107     }
108   }
109 
110   return nullptr;
111 }
112 
operator >(const TimerRequest & request) const113 bool TimerPool::TimerRequest::operator>(const TimerRequest& request) const {
114   return (expirationTime > request.expirationTime);
115 }
116 
generateTimerHandle()117 TimerHandle TimerPool::generateTimerHandle() {
118   TimerHandle timerHandle;
119   if (mGenerateTimerHandleMustCheckUniqueness) {
120     timerHandle = generateUniqueTimerHandle();
121   } else {
122     timerHandle = mLastTimerHandle + 1;
123     if (timerHandle == CHRE_TIMER_INVALID) {
124       // TODO: Consider that uniqueness checking can be reset when the number of
125       // timer requests reaches zero.
126       mGenerateTimerHandleMustCheckUniqueness = true;
127       timerHandle = generateUniqueTimerHandle();
128     }
129   }
130 
131   mLastTimerHandle = timerHandle;
132   return timerHandle;
133 }
134 
generateUniqueTimerHandle()135 TimerHandle TimerPool::generateUniqueTimerHandle() {
136   size_t timerHandle = mLastTimerHandle;
137   while (1) {
138     timerHandle++;
139     if (timerHandle != CHRE_TIMER_INVALID) {
140       TimerRequest *timerRequest = getTimerRequestByTimerHandle(timerHandle);
141       if (timerRequest == nullptr) {
142         return timerHandle;
143       }
144     }
145   }
146 }
147 
insertTimerRequest(const TimerRequest & timerRequest)148 void TimerPool::insertTimerRequest(const TimerRequest& timerRequest) {
149   // If the timer request was not inserted, simply append it to the list.
150   if (!mTimerRequests.push(timerRequest)) {
151     FATAL_ERROR("Failed to insert a timer request: out of memory");
152   }
153 }
154 
onSystemTimerCallback()155 void TimerPool::onSystemTimerCallback() {
156   // Gain exclusive access to the timer pool. This is needed because the context
157   // of this callback is not defined.
158   LockGuard<Mutex> lock(mMutex);
159   if (!handleExpiredTimersAndScheduleNext()) {
160     LOGE("Timer callback invoked with no outstanding timers");
161   }
162 }
163 
handleExpiredTimersAndScheduleNext()164 bool TimerPool::handleExpiredTimersAndScheduleNext() {
165   bool eventWasPosted = false;
166   while (!mTimerRequests.empty()) {
167     Nanoseconds currentTime = SystemTime::getMonotonicTime();
168     TimerRequest& currentTimerRequest = mTimerRequests.top();
169     if (currentTime >= currentTimerRequest.expirationTime) {
170       // Post an event for an expired timer.
171       mEventLoop.postEvent(CHRE_EVENT_TIMER,
172           const_cast<void *>(currentTimerRequest.cookie), nullptr,
173           kSystemInstanceId,
174           currentTimerRequest.requestingNanoapp->getInstanceId());
175       eventWasPosted = true;
176 
177       // Reschedule the timer if needed.
178       if (!currentTimerRequest.isOneShot) {
179         currentTimerRequest.expirationTime = currentTime
180             + currentTimerRequest.duration;
181         insertTimerRequest(currentTimerRequest);
182       }
183 
184       // Release the current request.
185       mTimerRequests.pop();
186     } else {
187       Nanoseconds duration = currentTimerRequest.expirationTime - currentTime;
188       mSystemTimer.set(handleSystemTimerCallback, this, duration);
189       break;
190     }
191   }
192 
193   return eventWasPosted;
194 }
195 
handleSystemTimerCallback(void * timerPoolPtr)196 void TimerPool::handleSystemTimerCallback(void *timerPoolPtr) {
197   // Cast the context pointer to a TimerPool context and call into the callback
198   // handler.
199   TimerPool *timerPool = static_cast<TimerPool *>(timerPoolPtr);
200   timerPool->onSystemTimerCallback();
201 }
202 
203 }  // namespace chre
204