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/fatal_error.h" 24 #include "chre/platform/log.h" 25 #include "chre/platform/system_time.h" 26 #include "chre/util/conditional_lock_guard.h" 27 #include "chre/util/lock_guard.h" 28 #include "chre/util/system/debug_dump.h" 29 #include "chre/util/time.h" 30 #include "chre_api/chre/version.h" 31 32 namespace chre { 33 34 // Out of line declaration required for nonintegral static types 35 constexpr Nanoseconds EventLoop::kIntervalWakeupBucket; 36 37 namespace { 38 39 /** 40 * Populates a chreNanoappInfo structure using info from the given Nanoapp 41 * instance. 42 * 43 * @param app A potentially null pointer to the Nanoapp to read from 44 * @param info The structure to populate - should not be null, but this function 45 * will handle that input 46 * 47 * @return true if neither app nor info were null, and info was populated 48 */ 49 bool populateNanoappInfo(const Nanoapp *app, struct chreNanoappInfo *info) { 50 bool success = false; 51 52 if (app != nullptr && info != nullptr) { 53 info->appId = app->getAppId(); 54 info->version = app->getAppVersion(); 55 info->instanceId = app->getInstanceId(); 56 success = true; 57 } 58 59 return success; 60 } 61 62 } // anonymous namespace 63 64 bool EventLoop::findNanoappInstanceIdByAppId(uint64_t appId, 65 uint32_t *instanceId) const { 66 CHRE_ASSERT(instanceId != nullptr); 67 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread()); 68 69 bool found = false; 70 for (const UniquePtr<Nanoapp> &app : mNanoapps) { 71 if (app->getAppId() == appId) { 72 *instanceId = app->getInstanceId(); 73 found = true; 74 break; 75 } 76 } 77 78 return found; 79 } 80 81 void EventLoop::forEachNanoapp(NanoappCallbackFunction *callback, void *data) { 82 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread()); 83 84 for (const UniquePtr<Nanoapp> &nanoapp : mNanoapps) { 85 callback(nanoapp.get(), data); 86 } 87 } 88 89 void EventLoop::invokeMessageFreeFunction(uint64_t appId, 90 chreMessageFreeFunction *freeFunction, 91 void *message, size_t messageSize) { 92 Nanoapp *nanoapp = lookupAppByAppId(appId); 93 if (nanoapp == nullptr) { 94 LOGE("Couldn't find app 0x%016" PRIx64 " for message free callback", appId); 95 } else { 96 auto prevCurrentApp = mCurrentApp; 97 mCurrentApp = nanoapp; 98 freeFunction(message, messageSize); 99 mCurrentApp = prevCurrentApp; 100 } 101 } 102 103 void EventLoop::run() { 104 LOGI("EventLoop start"); 105 106 bool havePendingEvents = false; 107 while (mRunning) { 108 // Events are delivered in two stages: first they arrive in the inbound 109 // event queue mEvents (potentially posted from another thread), then within 110 // this context these events are distributed to smaller event queues 111 // associated with each Nanoapp that should receive the event. Once the 112 // event is delivered to all interested Nanoapps, its free callback is 113 // invoked. 114 if (!havePendingEvents || !mEvents.empty()) { 115 if (mEvents.size() > mMaxEventPoolUsage) { 116 mMaxEventPoolUsage = mEvents.size(); 117 } 118 119 // mEvents.pop() will be a blocking call if mEvents.empty() 120 Event *event = mEvents.pop(); 121 // Need size() + 1 since the to-be-processed event has already been 122 // removed. 123 mPowerControlManager.preEventLoopProcess(mEvents.size() + 1); 124 distributeEvent(event); 125 } 126 127 havePendingEvents = deliverEvents(); 128 129 mPowerControlManager.postEventLoopProcess(mEvents.size()); 130 } 131 132 // Deliver any events sitting in Nanoapps' own queues (we could drop them to 133 // exit faster, but this is less code and should complete quickly under normal 134 // conditions), then purge the main queue of events pending distribution. All 135 // nanoapps should be prevented from sending events or messages at this point 136 // via currentNanoappIsStopping() returning true. 137 flushNanoappEventQueues(); 138 while (!mEvents.empty()) { 139 freeEvent(mEvents.pop()); 140 } 141 142 // Unload all running nanoapps 143 while (!mNanoapps.empty()) { 144 unloadNanoappAtIndex(mNanoapps.size() - 1); 145 } 146 147 LOGI("Exiting EventLoop"); 148 } 149 150 bool EventLoop::startNanoapp(UniquePtr<Nanoapp> &nanoapp) { 151 CHRE_ASSERT(!nanoapp.isNull()); 152 bool success = false; 153 auto *eventLoopManager = EventLoopManagerSingleton::get(); 154 EventLoop &eventLoop = eventLoopManager->getEventLoop(); 155 uint32_t existingInstanceId; 156 157 if (nanoapp.isNull()) { 158 // no-op, invalid argument 159 } else if (nanoapp->getTargetApiVersion() < 160 CHRE_FIRST_SUPPORTED_API_VERSION) { 161 LOGE("Incompatible nanoapp (target ver 0x%" PRIx32 162 ", first supported ver 0x%" PRIx32 ")", 163 nanoapp->getTargetApiVersion(), 164 static_cast<uint32_t>(CHRE_FIRST_SUPPORTED_API_VERSION)); 165 } else if (eventLoop.findNanoappInstanceIdByAppId(nanoapp->getAppId(), 166 &existingInstanceId)) { 167 LOGE("App with ID 0x%016" PRIx64 168 " already exists as instance ID 0x%" PRIx32, 169 nanoapp->getAppId(), existingInstanceId); 170 } else if (!mNanoapps.prepareForPush()) { 171 LOG_OOM(); 172 } else { 173 nanoapp->setInstanceId(eventLoopManager->getNextInstanceId()); 174 LOGD("Instance ID %" PRIu32 " assigned to app ID 0x%016" PRIx64, 175 nanoapp->getInstanceId(), nanoapp->getAppId()); 176 177 Nanoapp *newNanoapp = nanoapp.get(); 178 { 179 LockGuard<Mutex> lock(mNanoappsLock); 180 mNanoapps.push_back(std::move(nanoapp)); 181 // After this point, nanoapp is null as we've transferred ownership into 182 // mNanoapps.back() - use newNanoapp to reference it 183 } 184 185 mCurrentApp = newNanoapp; 186 success = newNanoapp->start(); 187 mCurrentApp = nullptr; 188 if (!success) { 189 // TODO: to be fully safe, need to purge/flush any events and messages 190 // sent by the nanoapp here (but don't call nanoappEnd). For now, we just 191 // destroy the Nanoapp instance. 192 LOGE("Nanoapp %" PRIu32 " failed to start", newNanoapp->getInstanceId()); 193 194 // Note that this lock protects against concurrent read and modification 195 // of mNanoapps, but we are assured that no new nanoapps were added since 196 // we pushed the new nanoapp 197 LockGuard<Mutex> lock(mNanoappsLock); 198 mNanoapps.pop_back(); 199 } else { 200 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STARTED, *newNanoapp); 201 } 202 } 203 204 return success; 205 } 206 207 bool EventLoop::unloadNanoapp(uint32_t instanceId, 208 bool allowSystemNanoappUnload) { 209 bool unloaded = false; 210 211 for (size_t i = 0; i < mNanoapps.size(); i++) { 212 if (instanceId == mNanoapps[i]->getInstanceId()) { 213 if (!allowSystemNanoappUnload && mNanoapps[i]->isSystemNanoapp()) { 214 LOGE("Refusing to unload system nanoapp"); 215 } else { 216 // Make sure all messages sent by this nanoapp at least have their 217 // associated free callback processing pending in the event queue (i.e. 218 // there are no messages pending delivery to the host) 219 EventLoopManagerSingleton::get() 220 ->getHostCommsManager() 221 .flushMessagesSentByNanoapp(mNanoapps[i]->getAppId()); 222 223 // Distribute all inbound events we have at this time - here we're 224 // interested in handling any message free callbacks generated by 225 // flushMessagesSentByNanoapp() 226 flushInboundEventQueue(); 227 228 // Mark that this nanoapp is stopping early, so it can't send events or 229 // messages during the nanoapp event queue flush 230 mStoppingNanoapp = mNanoapps[i].get(); 231 232 // Process any pending events, with the intent of ensuring that we free 233 // all events generated by this nanoapp 234 flushNanoappEventQueues(); 235 236 // Post the unload event now (so we can reference the Nanoapp instance 237 // directly), but nanoapps won't get it until after the unload completes 238 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STOPPED, *mStoppingNanoapp); 239 240 // Finally, we are at a point where there should not be any pending 241 // events or messages sent by the app that could potentially reference 242 // the nanoapp's memory, so we are safe to unload it 243 unloadNanoappAtIndex(i); 244 mStoppingNanoapp = nullptr; 245 246 // TODO: right now we assume that the nanoapp will clean up all of its 247 // resource allocations in its nanoappEnd callback (memory, sensor 248 // subscriptions, etc.), otherwise we're leaking resources. We should 249 // perform resource cleanup automatically here to avoid these types of 250 // potential leaks. 251 252 LOGD("Unloaded nanoapp with instanceId %" PRIu32, instanceId); 253 unloaded = true; 254 } 255 break; 256 } 257 } 258 259 return unloaded; 260 } 261 262 void EventLoop::postEventOrDie(uint16_t eventType, void *eventData, 263 chreEventCompleteFunction *freeCallback, 264 uint32_t targetInstanceId, 265 uint16_t targetGroupMask) { 266 if (mRunning) { 267 if (!allocateAndPostEvent(eventType, eventData, freeCallback, 268 kSystemInstanceId, targetInstanceId, 269 targetGroupMask)) { 270 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType); 271 } 272 } else if (freeCallback != nullptr) { 273 freeCallback(eventType, eventData); 274 } 275 } 276 277 bool EventLoop::postSystemEvent(uint16_t eventType, void *eventData, 278 SystemEventCallbackFunction *callback, 279 void *extraData) { 280 if (mRunning) { 281 Event *event = 282 mEventPool.allocate(eventType, eventData, callback, extraData); 283 284 if (event == nullptr || !mEvents.push(event)) { 285 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType); 286 } 287 return true; 288 } 289 return false; 290 } 291 292 bool EventLoop::postLowPriorityEventOrFree( 293 uint16_t eventType, void *eventData, 294 chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId, 295 uint32_t targetInstanceId, uint16_t targetGroupMask) { 296 bool eventPosted = false; 297 298 if (mRunning) { 299 if (mEventPool.getFreeBlockCount() > kMinReservedHighPriorityEventCount) { 300 eventPosted = allocateAndPostEvent(eventType, eventData, freeCallback, 301 senderInstanceId, targetInstanceId, 302 targetGroupMask); 303 if (!eventPosted) { 304 LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu32, 305 eventType, targetInstanceId); 306 } 307 } 308 } 309 310 if (!eventPosted && freeCallback != nullptr) { 311 freeCallback(eventType, eventData); 312 } 313 314 return eventPosted; 315 } 316 317 void EventLoop::stop() { 318 auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { 319 auto *obj = static_cast<EventLoop *>(data); 320 obj->onStopComplete(); 321 }; 322 323 // Stop accepting new events and tell the main loop to finish 324 postSystemEvent(static_cast<uint16_t>(SystemCallbackType::Shutdown), 325 /*eventData=*/this, callback, /*extraData=*/nullptr); 326 } 327 328 void EventLoop::onStopComplete() { 329 mRunning = false; 330 } 331 332 Nanoapp *EventLoop::findNanoappByInstanceId(uint32_t instanceId) const { 333 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread()); 334 return lookupAppByInstanceId(instanceId); 335 } 336 337 bool EventLoop::populateNanoappInfoForAppId( 338 uint64_t appId, struct chreNanoappInfo *info) const { 339 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread()); 340 Nanoapp *app = lookupAppByAppId(appId); 341 return populateNanoappInfo(app, info); 342 } 343 344 bool EventLoop::populateNanoappInfoForInstanceId( 345 uint32_t instanceId, struct chreNanoappInfo *info) const { 346 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread()); 347 Nanoapp *app = lookupAppByInstanceId(instanceId); 348 return populateNanoappInfo(app, info); 349 } 350 351 bool EventLoop::currentNanoappIsStopping() const { 352 return (mCurrentApp == mStoppingNanoapp || !mRunning); 353 } 354 355 void EventLoop::logStateToBuffer(DebugDumpWrapper &debugDump) const { 356 debugDump.print("\nEvent Loop:\n"); 357 debugDump.print(" Max event pool usage: %zu/%zu\n", mMaxEventPoolUsage, 358 kMaxEventCount); 359 360 Nanoseconds timeSince = 361 SystemTime::getMonotonicTime() - mTimeLastWakeupBucketCycled; 362 uint64_t timeSinceMins = 363 timeSince.toRawNanoseconds() / kOneMinuteInNanoseconds; 364 uint64_t durationMins = 365 kIntervalWakeupBucket.toRawNanoseconds() / kOneMinuteInNanoseconds; 366 debugDump.print(" Nanoapp host wakeup tracking: cycled %" PRIu64 367 "mins ago, bucketDuration=%" PRIu64 "mins\n", 368 timeSinceMins, durationMins); 369 370 debugDump.print("\nNanoapps:\n"); 371 for (const UniquePtr<Nanoapp> &app : mNanoapps) { 372 app->logStateToBuffer(debugDump); 373 } 374 } 375 376 bool EventLoop::allocateAndPostEvent(uint16_t eventType, void *eventData, 377 chreEventCompleteFunction *freeCallback, 378 uint32_t senderInstanceId, 379 uint32_t targetInstanceId, 380 uint16_t targetGroupMask) { 381 bool success = false; 382 383 Event *event = 384 mEventPool.allocate(eventType, eventData, freeCallback, senderInstanceId, 385 targetInstanceId, targetGroupMask); 386 if (event != nullptr) { 387 success = mEvents.push(event); 388 } 389 390 return success; 391 } 392 393 bool EventLoop::deliverEvents() { 394 bool havePendingEvents = false; 395 396 // Do one loop of round-robin. We might want to have some kind of priority or 397 // time sharing in the future, but this should be good enough for now. 398 for (const UniquePtr<Nanoapp> &app : mNanoapps) { 399 if (app->hasPendingEvent()) { 400 havePendingEvents |= deliverNextEvent(app); 401 } 402 } 403 404 return havePendingEvents; 405 } 406 407 bool EventLoop::deliverNextEvent(const UniquePtr<Nanoapp> &app) { 408 // TODO: cleaner way to set/clear this? RAII-style? 409 mCurrentApp = app.get(); 410 Event *event = app->processNextEvent(); 411 mCurrentApp = nullptr; 412 413 if (event->isUnreferenced()) { 414 freeEvent(event); 415 } 416 417 return app->hasPendingEvent(); 418 } 419 420 void EventLoop::distributeEvent(Event *event) { 421 for (const UniquePtr<Nanoapp> &app : mNanoapps) { 422 if ((event->targetInstanceId == chre::kBroadcastInstanceId && 423 app->isRegisteredForBroadcastEvent(event->eventType, 424 event->targetAppGroupMask)) || 425 event->targetInstanceId == app->getInstanceId()) { 426 app->postEvent(event); 427 } 428 } 429 430 if (event->isUnreferenced()) { 431 // Log if an event unicast to a nanoapp isn't delivered, as this is could be 432 // a bug (e.g. something isn't properly keeping track of when nanoapps are 433 // unloaded), though it could just be a harmless transient issue (e.g. race 434 // condition with nanoapp unload, where we post an event to a nanoapp just 435 // after queues are flushed while it's unloading) 436 if (event->targetInstanceId != kBroadcastInstanceId && 437 event->targetInstanceId != kSystemInstanceId) { 438 LOGW("Dropping event 0x%" PRIx16 " from instanceId %" PRIu32 "->%" PRIu32, 439 event->eventType, event->senderInstanceId, event->targetInstanceId); 440 } 441 freeEvent(event); 442 } 443 } 444 445 void EventLoop::flushInboundEventQueue() { 446 while (!mEvents.empty()) { 447 distributeEvent(mEvents.pop()); 448 } 449 } 450 451 void EventLoop::flushNanoappEventQueues() { 452 while (deliverEvents()) 453 ; 454 } 455 456 void EventLoop::freeEvent(Event *event) { 457 if (event->hasFreeCallback()) { 458 // TODO: find a better way to set the context to the creator of the event 459 mCurrentApp = lookupAppByInstanceId(event->senderInstanceId); 460 event->invokeFreeCallback(); 461 mCurrentApp = nullptr; 462 } 463 464 mEventPool.deallocate(event); 465 } 466 467 Nanoapp *EventLoop::lookupAppByAppId(uint64_t appId) const { 468 for (const UniquePtr<Nanoapp> &app : mNanoapps) { 469 if (app->getAppId() == appId) { 470 return app.get(); 471 } 472 } 473 474 return nullptr; 475 } 476 477 Nanoapp *EventLoop::lookupAppByInstanceId(uint32_t instanceId) const { 478 // The system instance ID always has nullptr as its Nanoapp pointer, so can 479 // skip iterating through the nanoapp list for that case 480 if (instanceId != kSystemInstanceId) { 481 for (const UniquePtr<Nanoapp> &app : mNanoapps) { 482 if (app->getInstanceId() == instanceId) { 483 return app.get(); 484 } 485 } 486 } 487 488 return nullptr; 489 } 490 491 void EventLoop::notifyAppStatusChange(uint16_t eventType, 492 const Nanoapp &nanoapp) { 493 auto *info = memoryAlloc<chreNanoappInfo>(); 494 if (info == nullptr) { 495 LOG_OOM(); 496 } else { 497 info->appId = nanoapp.getAppId(); 498 info->version = nanoapp.getAppVersion(); 499 info->instanceId = nanoapp.getInstanceId(); 500 501 postEventOrDie(eventType, info, freeEventDataCallback); 502 } 503 } 504 505 void EventLoop::unloadNanoappAtIndex(size_t index) { 506 const UniquePtr<Nanoapp> &nanoapp = mNanoapps[index]; 507 508 // Lock here to prevent the nanoapp instance from being accessed between the 509 // time it is ended and fully erased 510 LockGuard<Mutex> lock(mNanoappsLock); 511 512 // Let the app know it's going away 513 mCurrentApp = nanoapp.get(); 514 nanoapp->end(); 515 mCurrentApp = nullptr; 516 517 // Destroy the Nanoapp instance 518 mNanoapps.erase(index); 519 } 520 521 void EventLoop::handleNanoappWakeupBuckets() { 522 Nanoseconds now = SystemTime::getMonotonicTime(); 523 Nanoseconds duration = now - mTimeLastWakeupBucketCycled; 524 if (duration > kIntervalWakeupBucket) { 525 size_t numBuckets = static_cast<size_t>( 526 duration.toRawNanoseconds() / kIntervalWakeupBucket.toRawNanoseconds()); 527 mTimeLastWakeupBucketCycled = now; 528 for (auto &nanoapp : mNanoapps) { 529 nanoapp->cycleWakeupBuckets(numBuckets); 530 } 531 } 532 } 533 534 } // namespace chre 535