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
19 #include "chre/core/event.h"
20 #include "chre/core/event_loop_manager.h"
21 #include "chre/core/nanoapp.h"
22 #include "chre/platform/context.h"
23 #include "chre/platform/log.h"
24 #include "chre_api/chre/version.h"
25
26 namespace chre {
27
EventLoop()28 EventLoop::EventLoop()
29 : mTimerPool(*this) {}
30
findNanoappInstanceIdByAppId(uint64_t appId,uint32_t * instanceId)31 bool EventLoop::findNanoappInstanceIdByAppId(uint64_t appId,
32 uint32_t *instanceId) {
33 CHRE_ASSERT(instanceId != nullptr);
34
35 // TODO: would be nice to have a ConditionalLockGuard where we just pass this
36 // bool to the constructor and it automatically handles the unlock for us
37 bool needLock = (getCurrentEventLoop() != this);
38 if (needLock) {
39 mNanoappsLock.lock();
40 }
41
42 bool found = false;
43 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
44 if (app->getAppId() == appId) {
45 *instanceId = app->getInstanceId();
46 found = true;
47 break;
48 }
49 }
50
51 if (needLock) {
52 mNanoappsLock.unlock();
53 }
54
55 return found;
56 }
57
forEachNanoapp(NanoappCallbackFunction * callback,void * data)58 void EventLoop::forEachNanoapp(NanoappCallbackFunction *callback, void *data) {
59 bool needLock = (getCurrentEventLoop() != this);
60 if (needLock) {
61 mNanoappsLock.lock();
62 }
63
64 for (const UniquePtr<Nanoapp>& nanoapp : mNanoapps) {
65 callback(nanoapp.get(), data);
66 }
67
68 if (needLock) {
69 mNanoappsLock.unlock();
70 }
71 }
72
run()73 void EventLoop::run() {
74 LOGI("EventLoop start");
75 mRunning = true;
76
77 bool havePendingEvents = false;
78 while (mRunning) {
79 // TODO: document the two-stage event delivery process further... general
80 // idea is we block in mEvents.pop() if we know that no apps have pending
81 // events
82 if (!havePendingEvents || !mEvents.empty()) {
83 // TODO: this is *not* thread-safe; if we have multiple EventLoops, then
84 // there is no safety mechanism that ensures an event is not freed twice,
85 // or that its free callback is invoked in the proper EventLoop, etc.
86 Event *event = mEvents.pop();
87 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
88 if ((event->targetInstanceId == chre::kBroadcastInstanceId
89 && app->isRegisteredForBroadcastEvent(event->eventType))
90 || event->targetInstanceId == app->getInstanceId()) {
91 app->postEvent(event);
92 }
93 }
94
95 if (event->isUnreferenced()) {
96 // Events sent to the system instance ID are processed via the free
97 // callback and are not expected to be delivered to any nanoapp, so no
98 // need to log a warning in that case
99 if (event->senderInstanceId != kSystemInstanceId) {
100 LOGW("Dropping event 0x%" PRIx16, event->eventType);
101 }
102 freeEvent(event);
103 }
104 }
105
106 // TODO: most basic round-robin implementation - we might want to have some
107 // kind of priority in the future, but this should be good enough for now
108 havePendingEvents = false;
109 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
110 if (app->hasPendingEvent()) {
111 havePendingEvents |= deliverNextEvent(app);
112 }
113 }
114 }
115
116 // Drop any events pending distribution
117 while (!mEvents.empty()) {
118 freeEvent(mEvents.pop());
119 }
120
121 // Stop all running nanoapps
122 while (!mNanoapps.empty()) {
123 stopNanoapp(mNanoapps.size() - 1);
124 }
125
126 LOGI("Exiting EventLoop");
127 }
128
startNanoapp(UniquePtr<Nanoapp> & nanoapp)129 bool EventLoop::startNanoapp(UniquePtr<Nanoapp>& nanoapp) {
130 CHRE_ASSERT(!nanoapp.isNull());
131 bool success = false;
132 auto *eventLoopManager = EventLoopManagerSingleton::get();
133 uint32_t existingInstanceId;
134
135 if (nanoapp.isNull()) {
136 // no-op, invalid argument
137 } else if (eventLoopManager->findNanoappInstanceIdByAppId(nanoapp->getAppId(),
138 &existingInstanceId,
139 nullptr)) {
140 LOGE("App with ID 0x%016" PRIx64 " already exists as instance ID 0x%"
141 PRIx32, nanoapp->getAppId(), existingInstanceId);
142 } else if (!mNanoapps.prepareForPush()) {
143 LOGE("Failed to allocate space for new nanoapp");
144 } else {
145 nanoapp->setInstanceId(eventLoopManager->getNextInstanceId());
146 mCurrentApp = nanoapp.get();
147 success = nanoapp->start();
148 mCurrentApp = nullptr;
149 if (!success) {
150 LOGE("Nanoapp %" PRIu32 " failed to start", nanoapp->getInstanceId());
151 } else {
152 LockGuard<Mutex> lock(mNanoappsLock);
153 mNanoapps.push_back(std::move(nanoapp));
154 }
155 }
156
157 return success;
158 }
159
stopNanoapp(Nanoapp * nanoapp)160 void EventLoop::stopNanoapp(Nanoapp *nanoapp) {
161 for (size_t i = 0; i < mNanoapps.size(); i++) {
162 if (nanoapp == mNanoapps[i].get()) {
163 stopNanoapp(i);
164 return;
165 }
166 }
167
168 CHRE_ASSERT_LOG(false,
169 "Attempted to stop a nanoapp that is not already running");
170 }
171
postEvent(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint32_t senderInstanceId,uint32_t targetInstanceId)172 bool EventLoop::postEvent(uint16_t eventType, void *eventData,
173 chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId,
174 uint32_t targetInstanceId) {
175 bool success = false;
176
177 if (mRunning) {
178 Event *event = mEventPool.allocate(eventType, eventData, freeCallback,
179 senderInstanceId, targetInstanceId);
180 if (event != nullptr) {
181 success = mEvents.push(event);
182 } else {
183 LOGE("Failed to allocate event");
184 }
185 }
186
187 return success;
188 }
189
stop()190 void EventLoop::stop() {
191 postEvent(0, nullptr, nullptr, kSystemInstanceId, kSystemInstanceId);
192 // Stop accepting new events and tell the main loop to finish
193 mRunning = false;
194 }
195
getCurrentNanoapp() const196 Nanoapp *EventLoop::getCurrentNanoapp() const {
197 CHRE_ASSERT(getCurrentEventLoop() == this);
198 return mCurrentApp;
199 }
200
getNanoappCount() const201 size_t EventLoop::getNanoappCount() const {
202 CHRE_ASSERT(getCurrentEventLoop() == this);
203 return mNanoapps.size();
204 }
205
getTimerPool()206 TimerPool& EventLoop::getTimerPool() {
207 return mTimerPool;
208 }
209
findNanoappByInstanceId(uint32_t instanceId)210 Nanoapp *EventLoop::findNanoappByInstanceId(uint32_t instanceId) {
211 bool needLock = (getCurrentEventLoop() != this);
212 if (needLock) {
213 mNanoappsLock.lock();
214 }
215
216 Nanoapp *nanoapp = lookupAppByInstanceId(instanceId);
217
218 if (needLock) {
219 mNanoappsLock.unlock();
220 }
221
222 return nanoapp;
223 }
224
freeEvent(Event * event)225 void EventLoop::freeEvent(Event *event) {
226 if (event->freeCallback != nullptr) {
227 // TODO: find a better way to set the context to the creator of the event
228 mCurrentApp = lookupAppByInstanceId(event->senderInstanceId);
229 event->freeCallback(event->eventType, event->eventData);
230 mCurrentApp = nullptr;
231
232 mEventPool.deallocate(event);
233 }
234 }
235
deliverNextEvent(const UniquePtr<Nanoapp> & app)236 bool EventLoop::deliverNextEvent(const UniquePtr<Nanoapp>& app) {
237 // TODO: cleaner way to set/clear this? RAII-style?
238 mCurrentApp = app.get();
239 Event *event = app->processNextEvent();
240 mCurrentApp = nullptr;
241
242 if (event->isUnreferenced()) {
243 freeEvent(event);
244 }
245
246 return app->hasPendingEvent();
247 }
248
lookupAppByInstanceId(uint32_t instanceId)249 Nanoapp *EventLoop::lookupAppByInstanceId(uint32_t instanceId) {
250 // The system instance ID always has nullptr as its Nanoapp pointer, so can
251 // skip iterating through the nanoapp list for that case
252 if (instanceId != kSystemInstanceId) {
253 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
254 if (app->getInstanceId() == instanceId) {
255 return app.get();
256 }
257 }
258 }
259
260 return nullptr;
261 }
262
stopNanoapp(size_t index)263 void EventLoop::stopNanoapp(size_t index) {
264 const UniquePtr<Nanoapp>& nanoapp = mNanoapps[index];
265
266 // Process any events pending in this app's queue. Note that since we're
267 // running in the context of this EventLoop, no new events will be added to
268 // this nanoapp's event queue while we're doing this, so once it's empty, we
269 // can be assured it will stay that way.
270 while (nanoapp->hasPendingEvent()) {
271 deliverNextEvent(nanoapp);
272 }
273
274 // TODO: to safely stop a nanoapp while the EventLoop is still running, we
275 // need to deliver/purge any events that the nanoapp sent itself prior to
276 // calling end(), so that we won't try to invoke a free callback after
277 // unloading the nanoapp where that callback resides. Likewise, we need to
278 // make sure any messages to the host from this nanoapp are flushed as well.
279
280 // Let the app know it's going away
281 mCurrentApp = nanoapp.get();
282 nanoapp->end();
283 mCurrentApp = nullptr;
284
285 // Destroy the Nanoapp instance
286 {
287 LockGuard<Mutex> lock(mNanoappsLock);
288 mNanoapps.erase(index);
289 }
290 }
291
292 } // namespace chre
293