blob: 51c0c672c7f42ef9c48b462df74d976a488058b9 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "chre/core/event_loop.h"
#include "chre/core/event.h"
#include "chre/core/nanoapp.h"
#include "chre/platform/context.h"
#include "chre/platform/log.h"
#include "chre_api/chre/version.h"
namespace chre {
EventLoop::EventLoop()
: mTimerPool(*this) {}
bool EventLoop::findNanoappInstanceIdByAppId(uint64_t appId,
uint32_t *instanceId) {
CHRE_ASSERT(instanceId != nullptr);
// TODO: would be nice to have a ConditionalLockGuard where we just pass this
// bool to the constructor and it automatically handles the unlock for us
bool needLock = (getCurrentEventLoop() != this);
if (needLock) {
mNanoappsLock.lock();
}
bool found = false;
for (auto& app : mNanoapps) {
if (app->getAppId() == appId) {
*instanceId = app->getInstanceId();
found = true;
break;
}
}
if (needLock) {
mNanoappsLock.unlock();
}
return found;
}
void EventLoop::forEachNanoapp(NanoappCallbackFunction *callback, void *data) {
bool needLock = (getCurrentEventLoop() != this);
if (needLock) {
mNanoappsLock.lock();
}
for (const auto& nanoapp : mNanoapps) {
callback(nanoapp.get(), data);
}
if (needLock) {
mNanoappsLock.unlock();
}
}
void EventLoop::run() {
LOGI("EventLoop start");
mRunning = true;
bool havePendingEvents = false;
while (mRunning) {
// TODO: document the two-stage event delivery process further... general
// idea is we block in mEvents.pop() if we know that no apps have pending
// events
if (!havePendingEvents || !mEvents.empty()) {
// TODO: this is *not* thread-safe; if we have multiple EventLoops, then
// there is no safety mechanism that ensures an event is not freed twice,
// or that its free callback is invoked in the proper EventLoop, etc.
Event *event = mEvents.pop();
for (auto& app : mNanoapps) {
if ((event->targetInstanceId == chre::kBroadcastInstanceId
&& app->isRegisteredForBroadcastEvent(event->eventType))
|| event->targetInstanceId == app->getInstanceId()) {
app->postEvent(event);
}
}
if (event->isUnreferenced()) {
// Events sent to the system instance ID are processed via the free
// callback and are not expected to be delivered to any nanoapp, so no
// need to log a warning in that case
if (event->senderInstanceId != kSystemInstanceId) {
LOGW("Dropping event 0x%" PRIx16, event->eventType);
}
freeEvent(event);
}
}
// TODO: most basic round-robin implementation - we might want to have some
// kind of priority in the future, but this should be good enough for now
havePendingEvents = false;
for (auto& app : mNanoapps) {
if (app->hasPendingEvent()) {
// TODO: cleaner way to set/clear this? RAII-style?
mCurrentApp = app.get();
Event *event = app->processNextEvent();
mCurrentApp = nullptr;
if (event->isUnreferenced()) {
freeEvent(event);
}
havePendingEvents |= app->hasPendingEvent();
}
}
}
while (!mNanoapps.empty()) {
stopNanoapp(mNanoapps.front().get());
}
LOGI("Exiting EventLoop");
// TODO: need to purge/cleanup events, etc.
}
bool EventLoop::startNanoapp(PlatformNanoapp *platformNanoapp) {
CHRE_ASSERT(platformNanoapp != nullptr);
bool success = false;
if (!mNanoapps.prepareForPush()) {
LOGE("Failed to allocate space for new nanoapp");
} else {
// TODO: get these parameters from somewhere
UniquePtr<Nanoapp> nanoapp(0, 1, CHRE_API_VERSION_1_1, getNextInstanceId(),
true, platformNanoapp);
if (nanoapp.isNull()) {
LOGE("Failed to allocate new nanoapp");
} else {
mCurrentApp = nanoapp.get();
success = nanoapp->start();
mCurrentApp = nullptr;
if (!success) {
LOGE("Nanoapp %" PRIu32 " failed to start", nanoapp->getInstanceId());
} else {
LockGuard<Mutex> lock(mNanoappsLock);
mNanoapps.push_back(std::move(nanoapp));
}
}
}
return success;
}
void EventLoop::stopNanoapp(Nanoapp *nanoapp) {
CHRE_ASSERT(nanoapp != nullptr);
for (size_t i = 0; i < mNanoapps.size(); i++) {
if (nanoapp == mNanoapps[i].get()) {
{
LockGuard<Mutex> lock(mNanoappsLock);
mNanoapps.erase(i);
}
mCurrentApp = nanoapp;
nanoapp->stop();
mCurrentApp = nullptr;
return;
}
}
CHRE_ASSERT_LOG(false,
"Attempted to stop a nanoapp that is not already running");
}
bool EventLoop::postEvent(uint16_t eventType, void *eventData,
chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId,
uint32_t targetInstanceId) {
// TODO: Consider adding a CHRE_ASSERT(mRunning) here.
bool success = false;
Event *event = mEventPool.allocate(eventType, eventData, freeCallback,
senderInstanceId, targetInstanceId);
if (event != nullptr) {
success = mEvents.push(event);
} else {
LOGE("Failed to allocate event");
}
return success;
}
void EventLoop::stop() {
postEvent(0, nullptr, nullptr, kSystemInstanceId, kSystemInstanceId);
mRunning = false;
}
Nanoapp *EventLoop::getCurrentNanoapp() const {
CHRE_ASSERT(getCurrentEventLoop() == this);
return mCurrentApp;
}
size_t EventLoop::getNanoappCount() const {
CHRE_ASSERT(getCurrentEventLoop() == this);
return mNanoapps.size();
}
uint32_t EventLoop::getNextInstanceId() {
// This is a simple unique ID generator that checks a newly generated ID
// against all existing IDs using a search (currently linear search).
// Instance ID generation will slow with more apps. We generally expect there
// to be few apps and IDs are generated infrequently.
//
// The benefit of generating IDs this way is that there is no memory overhead
// to track issued IDs.
uint32_t nextInstanceId = mLastInstanceId + 1;
while (nextInstanceId == kSystemInstanceId
|| nextInstanceId == kBroadcastInstanceId
|| lookupAppByInstanceId(nextInstanceId) != nullptr) {
nextInstanceId++;
}
mLastInstanceId = nextInstanceId;
return nextInstanceId;
}
TimerPool& EventLoop::getTimerPool() {
return mTimerPool;
}
Nanoapp *EventLoop::findNanoappByInstanceId(uint32_t instanceId) {
bool needLock = (getCurrentEventLoop() != this);
if (needLock) {
mNanoappsLock.lock();
}
Nanoapp *nanoapp = lookupAppByInstanceId(instanceId);
if (needLock) {
mNanoappsLock.unlock();
}
return nanoapp;
}
Nanoapp *EventLoop::lookupAppByInstanceId(uint32_t instanceId) {
// The system instance ID always has nullptr as its Nanoapp pointer, so can
// skip iterating through the nanoapp list for that case
if (instanceId != kSystemInstanceId) {
for (auto& app : mNanoapps) {
if (app->getInstanceId() == instanceId) {
return app.get();
}
}
}
return nullptr;
}
void EventLoop::freeEvent(Event *event) {
if (event->freeCallback != nullptr) {
// TODO: find a better way to set the context to the creator of the event
mCurrentApp = lookupAppByInstanceId(event->senderInstanceId);
event->freeCallback(event->eventType, event->eventData);
mCurrentApp = nullptr;
mEventPool.deallocate(event);
}
}
} // namespace chre