blob: 3fd7e3b7f5095cedd41345cdcb1090f510e7d98d [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.
*/
#ifndef CHRE_CORE_EVENT_LOOP_H_
#define CHRE_CORE_EVENT_LOOP_H_
#include "chre/core/event.h"
#include "chre/core/nanoapp.h"
#include "chre/core/timer_pool.h"
#include "chre/platform/atomic.h"
#include "chre/platform/mutex.h"
#include "chre/platform/platform_nanoapp.h"
#include "chre/platform/power_control_manager.h"
#include "chre/util/dynamic_vector.h"
#include "chre/util/fixed_size_blocking_queue.h"
#include "chre/util/non_copyable.h"
#include "chre/util/synchronized_memory_pool.h"
#include "chre/util/unique_ptr.h"
#include "chre_api/chre/event.h"
// These default values can be overridden in the variant-specific makefile.
#ifndef CHRE_MAX_EVENT_COUNT
#define CHRE_MAX_EVENT_COUNT 96
#endif
#ifndef CHRE_MAX_UNSCHEDULED_EVENT_COUNT
#define CHRE_MAX_UNSCHEDULED_EVENT_COUNT 96
#endif
namespace chre {
/**
* The EventLoop represents a single thread of execution that is shared among
* zero or more nanoapps. As the name implies, the EventLoop is built around a
* loop that delivers events to the nanoapps managed within for processing.
*/
class EventLoop : public NonCopyable {
public:
EventLoop() : mRunning(true) {}
/**
* Synchronous callback used with forEachNanoapp
*/
typedef void (NanoappCallbackFunction)(const Nanoapp *nanoapp, void *data);
/**
* Searches the set of nanoapps managed by this EventLoop for one with the
* given app ID. If found, provides its instance ID, which can be used to send
* events to the app.
*
* This function is safe to call from any thread.
*
* @param appId The nanoapp identifier to search for.
* @param instanceId If this function returns true, will be populated with the
* instanceId associated with the given appId; otherwise unmodified.
* Must not be null.
* @return true if the given app ID was found and instanceId was populated
*/
bool findNanoappInstanceIdByAppId(uint64_t appId, uint32_t *instanceId) const;
/**
* Iterates over the list of Nanoapps managed by this EventLoop, and invokes
* the supplied callback for each one. This holds a lock if necessary, so it
* is safe to call from any thread.
*
* @param callback Function to invoke on each Nanoapp (synchronously)
* @param data Arbitrary data to pass to the callback
*/
void forEachNanoapp(NanoappCallbackFunction *callback, void *data);
/**
* Invokes a message to host free callback supplied by the given nanoapp
* (identified by app ID). Ensures that the calling context is updated
* appropriately.
*
* @param appId Identifies the nanoapp that sent this message and supplied the
* free callback
* @param freeFunction The non-null message free callback given by the nanoapp
* @param message Pointer to the message data
* @param messageSize Size of the message
*/
void invokeMessageFreeFunction(
uint64_t appId, chreMessageFreeFunction *freeFunction, void *message,
size_t messageSize);
/**
* Invokes the Nanoapp's start callback, and if successful, adds it to the
* set of Nanoapps managed by this EventLoop. This function must only be
* called from the context of the thread that runs this event loop (i.e. from
* the same thread that will call run() or from a callback invoked within
* run()).
*
* @param nanoapp The nanoapp that will be started. Upon success, this
* UniquePtr will become invalid, as the underlying Nanoapp instance
* will have been transferred to be managed by this EventLoop.
* @return true if the app was started successfully
*/
bool startNanoapp(UniquePtr<Nanoapp>& nanoapp);
/**
* Stops and unloads a nanoapp identified by its instance ID. The end entry
* point will be invoked, and the chre::Nanoapp instance will be destroyed.
* After this function returns, all references to the Nanoapp instance are
* invalidated.
*
* @param instanceId The nanoapp's unique instance identifier
* @param allowSystemNanoappUnload If false, this function will reject
* attempts to unload a system nanoapp
*
* @return true if the nanoapp with the given instance ID was found & unloaded
*/
bool unloadNanoapp(uint32_t instanceId, bool allowSystemNanoappUnload);
/**
* Executes the loop that blocks on the event queue and delivers received
* events to nanoapps. Only returns after stop() is called (from another
* context).
*/
void run();
/**
* Signals the event loop currently executing in run() to exit gracefully at
* the next available opportunity. This function is thread-safe.
*/
void stop();
/**
* Posts an event to a nanoapp that is currently running (or all nanoapps if
* the target instance ID is kBroadcastInstanceId). A senderInstanceId cannot
* be provided to this method because it should only be used to post events
* sent by the system. If the event fails to post, this is considered a fatal
* error.
*
* @see postLowPriorityEventOrFree
*/
bool postEventOrDie(uint16_t eventType, void *eventData,
chreEventCompleteFunction *freeCallback,
uint32_t targetInstanceId = kBroadcastInstanceId);
/**
* Posts an event to a nanoapp that is currently running (or all nanoapps if
* the target instance ID is kBroadcastInstanceId). If the event fails to
* post, it is freed with freeCallback.
*
* This function is safe to call from any thread.
*
* @param eventType The type of data being posted.
* @param eventData The data being posted.
* @param freeCallback The callback to invoke when the event is no longer
* needed.
* @param senderInstanceId The instance ID of the sender of this event.
* @param targetInstanceId The instance ID of the destination of this event.
*
* @return true if the event was successfully added to the queue.
*
* @see chreSendEvent
*/
bool postLowPriorityEventOrFree(
uint16_t eventType, void *eventData,
chreEventCompleteFunction *freeCallback,
uint32_t senderInstanceId = kSystemInstanceId,
uint32_t targetInstanceId = kBroadcastInstanceId);
/**
* Returns a pointer to the currently executing Nanoapp, or nullptr if none is
* currently executing. Must only be called from within the thread context
* associated with this EventLoop.
*
* @return the currently executing nanoapp, or nullptr
*/
Nanoapp *getCurrentNanoapp() const {
return mCurrentApp;
}
/**
* Gets the number of nanoapps currently associated with this event loop. Must
* only be called within the context of this EventLoop.
*
* @return The number of nanoapps managed by this event loop
*/
size_t getNanoappCount() const {
return mNanoapps.size();
}
/**
* Obtains the TimerPool associated with this event loop.
*
* @return The timer pool owned by this event loop.
*/
TimerPool& getTimerPool() {
return mTimerPool;
}
/**
* Searches the set of nanoapps managed by this EventLoop for one with the
* given instance ID.
*
* This function is safe to call from any thread.
*
* @param instanceId The nanoapp instance ID to search for.
* @return a pointer to the found nanoapp or nullptr if no match was found.
*/
Nanoapp *findNanoappByInstanceId(uint32_t instanceId) const;
/**
* Looks for an app with the given ID and if found, populates info with its
* metadata. Safe to call from any thread.
*
* @see chreGetNanoappInfoByAppId
*/
bool populateNanoappInfoForAppId(uint64_t appId,
struct chreNanoappInfo *info) const;
/**
* Looks for an app with the given instance ID and if found, populates info
* with its metadata. Safe to call from any thread.
*
* @see chreGetNanoappInfoByInstanceId
*/
bool populateNanoappInfoForInstanceId(uint32_t instanceId,
struct chreNanoappInfo *info) const;
/**
* @return true if the current Nanoapp (or entire CHRE) is being unloaded, and
* therefore it should not be allowed to send events or messages, etc.
*/
bool currentNanoappIsStopping() const;
/**
* Prints state in a string buffer. Must only be called from the context of
* the main CHRE thread.
*
* @param buffer Pointer to the start of the buffer.
* @param bufferPos Pointer to buffer position to start the print (in-out).
* @param size Size of the buffer in bytes.
*/
void logStateToBuffer(char *buffer, size_t *bufferPos,
size_t bufferSize) const;
/**
* Returns a reference to the power control manager. This allows power
* controls from subsystems outside the event loops.
*/
PowerControlManager& getPowerControlManager() {
return mPowerControlManager;
}
private:
//! The maximum number of events that can be active in the system.
static constexpr size_t kMaxEventCount = CHRE_MAX_EVENT_COUNT;
//! The minimum number of events to reserve in the event pool for high
//! priority events.
static constexpr size_t kMinReservedHighPriorityEventCount = 16;
//! The maximum number of events that are awaiting to be scheduled. These
//! events are in a queue to be distributed to apps.
static constexpr size_t kMaxUnscheduledEventCount =
CHRE_MAX_UNSCHEDULED_EVENT_COUNT;
//! The memory pool to allocate incoming events from.
SynchronizedMemoryPool<Event, kMaxEventCount> mEventPool;
//! The timer used schedule timed events for tasks running in this event loop.
TimerPool mTimerPool;
//! The list of nanoapps managed by this event loop.
DynamicVector<UniquePtr<Nanoapp>> mNanoapps;
//! This lock *must* be held whenever we:
//! (1) make changes to the mNanoapps vector, or
//! (2) read the mNanoapps vector from a thread other than the one
//! associated with this EventLoop
//! It is not necessary to acquire the lock when reading mNanoapps from within
//! the thread context of this EventLoop.
mutable Mutex mNanoappsLock;
//! The blocking queue of incoming events from the system that have not been
//! distributed out to apps yet.
FixedSizeBlockingQueue<Event *, kMaxUnscheduledEventCount> mEvents;
//! Indicates whether the event loop is running.
AtomicBool mRunning;
//! The nanoapp that is currently executing - must be set any time we call
//! into the nanoapp's entry points or callbacks
Nanoapp *mCurrentApp = nullptr;
//! Set to the nanoapp we are in the process of unloading in unloadNanoapp()
Nanoapp *mStoppingNanoapp = nullptr;
//! The object which manages power related controls.
PowerControlManager mPowerControlManager;
//! The maximum number of events ever waiting in the event pool.
size_t mMaxEventPoolUsage = 0;
/**
* Modifies the run loop state so it no longer iterates on new events. This
* should only be invoked by the event loop when it is ready to stop
* processing new events.
*/
void onStopComplete();
/**
* Allocates an event from the event pool and post it.
*
* @return true if the event has been successfully allocated and posted.
*
* @see postEventOrDie and postLowPriorityEventOrFree
*/
bool allocateAndPostEvent(uint16_t eventType, void *eventData,
chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId,
uint32_t targetInstanceId);
/**
* Do one round of Nanoapp event delivery, only considering events in
* Nanoapps' own queues (not mEvents).
*
* @return true if there are more events pending in Nanoapps' own queues
*/
bool deliverEvents();
/**
* Delivers the next event pending in the Nanoapp's queue, and takes care of
* freeing events once they have been delivered to all nanoapps. Must only be
* called after confirming that the app has at least 1 pending event.
*
* @return true if the nanoapp has another event pending in its queue
*/
bool deliverNextEvent(const UniquePtr<Nanoapp>& app);
/**
* Given an event pulled from the main incoming event queue (mEvents), deliver
* it to all Nanoapps that should receive the event, or free the event if
* there are no valid recipients.
*
* @param event The Event to distribute to Nanoapps
*/
void distributeEvent(Event *event);
/**
* Distribute all events pending in the inbound event queue. Note that this
* function only guarantees that any events in the inbound queue at the time
* it is called will be distributed to Nanoapp event queues - new events may
* still be posted during or after this function call from other threads as
* long as postEvent() will accept them.
*/
void flushInboundEventQueue();
/**
* Delivers events pending in Nanoapps' own queues until they are all empty.
*/
void flushNanoappEventQueues();
/**
* Call after when an Event has been delivered to all intended recipients.
* Invokes the event's free callback (if given) and releases resources.
*
* @param event The event to be freed
*/
void freeEvent(Event *event);
/**
* Finds a Nanoapp with the given 64-bit appId.
*
* Only safe to call within this EventLoop's thread, or if mNanoappsLock is
* held.
*
* @param appId Nanoapp ID
* @return Pointer to Nanoapp instance in this EventLoop with the given app
* ID, or nullptr if not found
*/
Nanoapp *lookupAppByAppId(uint64_t appId) const;
/**
* Finds a Nanoapp with the given instanceId.
*
* Only safe to call within this EventLoop's thread, or if mNanoappsLock is
* held.
*
* @param instanceId Nanoapp instance identifier
* @return Nanoapp with the given instanceId, or nullptr if not found
*/
Nanoapp *lookupAppByInstanceId(uint32_t instanceId) const;
/**
* Sends an event with payload struct chreNanoappInfo populated from the given
* Nanoapp instance to inform other nanoapps about it starting/stopping.
*
* @param eventType Should be one of CHRE_EVENT_NANOAPP_{STARTED, STOPPED}
* @param nanoapp The nanoapp instance whose status has changed
*/
void notifyAppStatusChange(uint16_t eventType, const Nanoapp& nanoapp);
/**
* Stops and unloads the Nanoapp at the given index in mNanoapps.
*
* IMPORTANT: prior to calling this function, the event queues must be in a
* safe condition for removal of this nanoapp. This means that there must not
* be any pending events in this nanoapp's queue, and there must not be any
* outstanding events sent by this nanoapp, as they may reference the
* nanoapp's own memory (even if there is no free callback).
*/
void unloadNanoappAtIndex(size_t index);
};
} // namespace chre
#endif // CHRE_CORE_EVENT_LOOP_H_