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 28 EventLoop::EventLoop() 29 : mTimerPool(*this) {} 30 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 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 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 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 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 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 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 196 Nanoapp *EventLoop::getCurrentNanoapp() const { 197 CHRE_ASSERT(getCurrentEventLoop() == this); 198 return mCurrentApp; 199 } 200 201 size_t EventLoop::getNanoappCount() const { 202 CHRE_ASSERT(getCurrentEventLoop() == this); 203 return mNanoapps.size(); 204 } 205 206 TimerPool& EventLoop::getTimerPool() { 207 return mTimerPool; 208 } 209 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 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 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 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 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