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