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