Event Queues

An event queue allows a task to serialize incoming events and simplify event processing. Events are stored in a queue and a task removes and processes an event from the queue. An event is processed in the context of this task. Events may be generated by OS callouts, interrupt handlers, and other tasks.

Description

Mynewt‘s event queue model uses callback functions to process events. Each event is associated with a callback function that is called to process the event. This model enables a library package, that uses events in its implementation but does not have real-time timing requirements, to use an application event queue instead of creating a dedicated event queue and task to process its events. The callback function executes in the context of the task that the application creates to manage the event queue. This model reduces an application’s memory requirement because memory must be allocated for the task's stack when a task is created. A package that has real-time timing requirements and must run at a specific task priority should create a dedicated event queue and task to process its events.

In the Mynewt model, a package defines its events and implements the callback functions for the events. A package that does not have real-time timing requirements should use Mynewt‘s default event queue for its events. The callback function for an event from the Mynewt default event queue is executed in the context of the application main task. A package can, optionally, export a function that allows an application to specify the event queue for the package to use. (See the example in the os_eventq_designate() function description on how to write such a function.) The application task handler that manages the event queue simply pulls events from the event queue and executes the event’s callback function in its context.

A common way that Mynewt applications or packages process events from an event queue is to have a task that executes in an infinite loop and calls the os_eventq_get() function to dequeue and return the event from the head of the event queue. The task then calls the event callback function to process the event. The os_eventq_get() function puts the task in to the sleeping state when there are no events on the queue. (See Scheduler for more information on task execution states.) Other tasks (or interrupts) call the os_eventq_put() function to add an event to the queue. The os_eventq_put() function determines whether a task is blocked waiting for an event on the queue and puts the task into the ready-to-run state.

A task can use the os_eventq_run() wrapper function that calls the os_eventq_get() function to dequeue an event from the queue and then calls the event callback function to process the event.

Note:

  • Only one task should consume or block waiting for events from an event queue.
  • The os_callout subsystem uses events for timer expiration notification.

Data structures

The os_event structure defines an event and has the following fields:

struct os_event {
    uint8_t ev_queued;
    os_event_fn *ev_cb;
    void *ev_arg;
    STAILQ_ENTRY(os_event) ev_next;
};
ElementDescription
ev_queuedInternal field that indicates whether this event is currently linked to an event queue
ev_cbPointer to the callback function to call to process this event
ev_argPointer to an optional opaque data that the callback function uses to process this event
ev_nextLinkage attaching this event to an event queue

An event callback function has the following function prototype:

typedef void os_event_fn(struct os_event *ev);

A pointer to the os_event structure for the event is passed as an argument to the callback function.

Notes: If the memory for the os_event structure is dynamically allocated:

  • You must not free the memory for an event that is currently on an event queue.
  • You must free the memory in the callback function after it completes processing the event.

You must set the callback function for an event when you initialize the event. For example, here is an example of a statically-initialized event in the NimBLE host:

static void ble_hs_event_tx_notify(struct os_event *ev);

/** OS event - triggers tx of pending notifications and indications. */
static struct os_event ble_hs_ev_tx_notifications = {
    .ev_cb = ble_hs_event_tx_notify,
};

struct os_eventq {
    struct os_task *evq_task;
    STAILQ_HEAD(, os_event) evq_list;
};
ElementDescription
evq_taskPointer to the task, if any, that is waiting (in the sleeping state) for the os_eventq_get() function to return an event
evq_listHead of the list of events in this queue

You must call the os_eventq_init() function to initialize an event queue before you can add events to the queue.

List of Functions

The functions available in the Event Queue feature are:

FunctionDescription
os_eventq_initInitializes an event queue.
os_eventq_initedIndicates whether an event queue has been initialized.
os_eventq_getDequeues an event from the head of an event queue. The calling task blocks (in the sleeping state) when the event queue is empty.
os_eventq_putEnqueues an event at the tail of an event queue and puts a task waiting for an event on the queue into the ready-to-run state.
os_eventq_removeRemoves an event from an event queue.
os_eventq_dflt_getGets the default event queue.
os_eventq_designateReassigns a package's current event queue to a new event queue.
os_eventq_runWrapper function that dequeues an event from an event queue and calls the callbck function for the event.