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