EventQueue
EventQueue class hierarchy
The EventQueue class provides a flexible queue for scheduling events. You can use the EventQueue class for synchronization between multiple threads, or to move events out of interrupt context (deferred execution of time consuming or non-ISR safe operations).
The EventQueue class is thread and ISR safe.
You can use the dispatch
and dispatch_forever
APIs to execute pending events. break_dispatch
is to terminate the execution of events in the specified EventQueue.
Shared event queues
Mbed OS provides two shared queues software can use. This can avoid the need to create private event dispatch threads and reduce the total amount of RAM used.
EventQueue class reference
Public Member Functions | |
EventQueue (unsigned size=(32 *((sizeof(struct equeue_event)+2 *sizeof(void *))-2 *sizeof(void *)+sizeof(mbed::Callback< void()>))), unsigned char *buffer=NULL) | |
Create an EventQueue. More... | |
~EventQueue () | |
Destroy an EventQueue. More... | |
void | dispatch (int ms=-1) |
Dispatch events. More... | |
void | dispatch_forever () |
Dispatch events without a timeout. More... | |
void | break_dispatch () |
Break out of a running event loop. More... | |
unsigned | tick () |
Millisecond counter. More... | |
void | cancel (int id) |
Cancel an in-flight event. More... | |
int | time_left (int id) |
Query how much time is left for delayed event. More... | |
void | background (mbed::Callback< void(int)> update) |
Background an event queue onto a single-shot timer-interrupt. More... | |
int | chain (EventQueue *target) |
Chain an event queue onto another event queue. More... | |
template<typename F , typename... Args> | |
int | call (F f, Args...args) |
Calls an event on the queue. More... | |
template<typename T , typename R , typename... Args> | |
int | call (T *obj, R(T::*method)(Args...args), Args...args) |
Calls an event on the queue. More... | |
template<typename F , typename... Args> | |
int | call_in (int ms, Args...args) |
Calls an event on the queue after a specified delay. More... | |
template<typename T , typename R , typename... Args> | |
int | call_in (int ms, T *obj, R(T::*method)(Args...args), Args...args) |
Calls an event on the queue after a specified delay. More... | |
template<typename F , typename... Args> | |
int | call_every (int ms, F f, Args...args) |
Calls an event on the queue periodically. More... | |
template<typename T , typename R , typename... Args> | |
int | call_every (int ms, T *obj, R(T::*method)(Args...args), Args...args) |
Calls an event on the queue periodically. More... | |
template<typename R , typename... BoundArgs, typename... Args> | |
Event< void(Args...)> | event (R(*func)(BoundArgs...), Args...args) |
Creates an event bound to the event queue. More... | |
template<typename T , typename R , typename... BoundArgs, typename... ContextArgs, typename... Args> | |
Event< void(Args...)> | event (T *obj, R(T::*method)(BoundArgs..., Args...), ContextArgs...context_args) |
Creates an event bound to the event queue. More... | |
template<typename R , typename... BoundArgs, typename... ContextArgs, typename... Args> | |
Event< void(Args...)> | event (mbed::Callback< R(BoundArgs..., Args...)> cb, ContextArgs...context_args) |
Creates an event bound to the event queue. More... |
Shared event queue reference
EventQueue example: deferring from interrupt context
The code executes two handler functions (rise_handler
and fall_handler
) in two different contexts:
- In interrupt context when a rising edge is detected on
SW2
(rise_handler
). - In the context of the event loop's thread function when a falling edge is detected on
SW2
(fall_handler
). You can use thefall_handler
function as an argument toqueue.event()
to specify thatfall_handler
runs in user context instead of interrupt context.
#include "mbed.h"
DigitalOut led1(LED1);
InterruptIn sw(SW2);
EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t;
void rise_handler(void) {
printf("rise_handler in context %p\r\n", Thread::gettid());
// Toggle LED
led1 = !led1;
}
void fall_handler(void) {
printf("fall_handler in context %p\r\n", Thread::gettid());
// Toggle LED
led1 = !led1;
}
int main() {
// Start the event queue
t.start(callback(&queue, &EventQueue::dispatch_forever));
printf("Starting in context %p\r\n", Thread::gettid());
// The 'rise' handler will execute in IRQ context
sw.rise(rise_handler);
// The 'fall' handler will execute in the context of thread 't'
sw.fall(queue.event(fall_handler));
}
Shared event example: deferring from interrupt context
Like the previous example, this defers from interrupt to an event queue thread. However, rather than creating its own thread, it uses the shared event queue – potentially sharing it with other system components and saving RAM.
As the event queue is shared, you should limit the execution time of your event functions to avoid delaying other users’ events excessively.
#include "mbed.h"
#include "mbed_events.h"
DigitalOut led1(LED1);
InterruptIn sw(SW2);
void rise_handler(void) {
// Toggle LED
led1 = !led1;
}
void fall_handler(void) {
printf("fall_handler in context %p\r\n", Thread::gettid());
// Toggle LED
led1 = !led1;
}
int main() {
// Request the shared queue
EventQueue *queue = mbed_event_queue();
printf("Starting in context %p\r\n", Thread::gettid());
// The 'rise' handler will execute in IRQ context
sw.rise(rise_handler);
// The 'fall' handler will execute in the context of the shared queue thread
sw.fall(queue->event(fall_handler));
}
EventQueue example: posting events to the queue
The code below demonstrates queueing functions to be called after a delay and queueing functions to be called periodically.
#include "mbed_events.h"
#include <stdio.h>
int main() {
// creates a queue with the default size
EventQueue queue;
// events are simple callbacks
queue.call(printf, "called immediately\n");
queue.call_in(2000, printf, "called in 2 seconds\n");
queue.call_every(1000, printf, "called every 1 seconds\n");
// events are executed by the dispatch method
queue.dispatch();
}
EventQueue example: chaining events from more than one queue
Event queues easily align with module boundaries, where event dispatch can implicitly synchronize internal state. Multiple modules can use independent event queues but still be composed through the EventQueue::chain
function.
#include "mbed_events.h"
#include <stdio.h>
/**
Event queues easily align with module boundaries, where internal state can be
implicitly synchronized through event dispatch. Multiple modules can use
independent event queues, but still be composed through the EventQueue::chain function.
**/
int main() {
// Create some event queues with pending events
EventQueue a;
a.call(printf, "hello from a!\n");
EventQueue b;
b.call(printf, "hello from b!\n");
EventQueue c;
c.call(printf, "hello from c!\n");
// Chain c and b onto a's event queue. Both c and b will be dispatched
// in the context of a's dispatch function.
c.chain(&a);
b.chain(&a);
// Dispatching a will in turn dispatch b and c, printing hello from
// all three queues
a.dispatch();
}
Shared event example: running the shared queue from main
To further save RAM, if you have no other work to do in your main function after initialization, you can dispatch the global event queue from there, avoiding the need to create a separate dispatch thread.
To do this, set the mbed_app.json
configuration option events.shared-dispatch-from-application
to true, and add a dispatch call to main, as in this example. (The prints now show the same context for startup and fall_handler
).
#include "mbed.h"
#include "mbed_events.h"
DigitalOut led1(LED1);
InterruptIn sw(SW2);
void rise_handler(void) {
// Toggle LED
led1 = !led1;
}
void fall_handler(void) {
printf("fall_handler in context %p\r\n", Thread::gettid());
// Toggle LED
led1 = !led1;
}
int main() {
// Request the shared queue
EventQueue *queue = mbed_event_queue();
printf("Starting in context %p\r\n", Thread::gettid());
// The 'rise' handler will execute in IRQ context
sw.rise(rise_handler);
// The 'fall' handler will execute in the context of the shared queue (actually the main thread)
sw.fall(queue->event(fall_handler));
// Setup complete, so we now dispatch the shared queue from main
queue->dispatch_forever();
}