Home | History | Annotate | Download | only in core
      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