Mistake on this page?
Report an issue in GitHub or email us

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_for, dispatch_once 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 *(EQUEUE_EVENT_SIZE-2 *sizeof(void *)+sizeof(mbed::Callback< void()>))), unsigned char *buffer=NULL)
 Create an EventQueue. More...
 ~EventQueue ()
 Destroy an EventQueue. More...
void dispatch_for (duration ms)
 Dispatch events. More...
void dispatch (int ms=-1)
 Dispatch events. More...
void dispatch_forever ()
 Dispatch events without a timeout. More...
void dispatch_once ()
 Dispatch currently queued events only and then terminate. More...
void break_dispatch ()
 Break out of a running event loop. More...
unsigned tick ()
 Millisecond counter. More...
bool cancel (int id)
 Cancel an in-flight event. More...
template<typename F , typename A >
bool cancel (UserAllocatedEvent< F, A > *event)
 Cancel an in-flight user allocated event. More...
int time_left (int id)
 Query how much time is left for delayed event. More...
template<typename F , typename A >
int time_left (UserAllocatedEvent< F, A > *event)
 Query how much time is left for delayed UserAllocatedEvent. 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... ArgTs>
int call_in (duration ms, F f, ArgTs...args)
 Calls an event on the queue after a specified delay. More...
template<typename T , typename R , typename... ArgTs>
int call_in (duration ms, T *obj, R(T::*method)(ArgTs...args), ArgTs...args)
 Calls an event on the queue after a specified delay. More...
template<typename F , typename... ArgTs>
int call_every (duration ms, F f, ArgTs...args)
 Calls an event on the queue periodically. More...
template<typename T , typename R , typename... ArgTs>
int call_every (duration ms, T *obj, R(T::*method)(ArgTs...args), ArgTs...args)
 Calls an event on the queue periodically. More...
template<typename R , typename... BoundArgTs, typename... ContextArgTs, typename... ArgTs>
Event< void(ArgTs...)> event (R(*func)(BoundArgTs..., ArgTs...), ContextArgTs...context_args)
 Creates an event bound to the event queue. More...
template<typename T , typename R , typename... BoundArgTs, typename... ContextArgTs, typename... ArgTs>
Event< void(ArgTs...)> event (T *obj, R(T::*method)(BoundArgTs..., ArgTs...), ContextArgTs...context_args)
 Creates an event bound to the event queue. More...
template<typename R , typename... BoundArgTs, typename... ContextArgTs, typename... ArgTs>
Event< void(ArgTs...)> event (mbed::Callback< R(BoundArgTs..., ArgTs...)> cb, ContextArgTs...context_args)
 Creates an event bound to the event queue. More...
template<typename F , typename... ArgTs>
UserAllocatedEvent< F, void(ArgTs...)> make_user_allocated_event (F f, ArgTs...args)
 Creates an user allocated event bound to the event queue. More...
template<typename T , typename R , typename... ArgTs>
UserAllocatedEvent< mbed::Callback< void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event (T *obj, R(T::*method)(ArgTs...args), ArgTs...args)
 Creates an user allocated 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:

  1. In interrupt context when a rising edge is detected on SW2 (rise_handler).
  2. In the context of the event loop's thread function when a falling edge is detected on SW2 (fall_handler). You can use the fall_handler function as an argument to queue.event() to specify that fall_handler runs in user context instead of interrupt context.
/*
 * Copyright (c) 2020 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 */
#include "mbed.h"

DigitalOut led1(LED1);
InterruptIn sw(SW2);
EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t;


void rise_handler(void)
{
    queue.call(printf, "rise_handler in context %p\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

void fall_handler(void)
{
    printf("rise_handler in context %p\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

int main()
{
    // Start the event queue
    t.start(callback(&queue, &EventQueue::dispatch_forever));
    printf("Starting in context %p\r\n", ThisThread::get_id());
    // 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.

/*
 * Copyright (c) 2020 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 */
#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", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

int main()
{
    // Request the shared queue
    EventQueue *queue = mbed_event_queue();
    printf("Starting in context %p\r\n", ThisThread::get_id());
    // 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.

/*
 * Copyright (c) 2020 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 */
#include "mbed_events.h"
#include <stdio.h>
using namespace std::chrono_literals;

// In the example below there are 3 types of event callbacks
// 1) Call the provided function immediately
// 2) Call the provided function once (after the specified period)
// 3) Call the provided function every specified period
//
// Expected output:
//
// called immediately
// called every 1 seconds
// called in 2 seconds
// called every 1 seconds
// called every 1 seconds
//    ^ repeated forever
//
int main()
{
    // creates a queue with the default size
    EventQueue queue;

    // events are simple callbacks
    queue.call(printf, "called immediately\n");
    queue.call_in(2000ms, printf, "called in 2 seconds\n");
    queue.call_every(1000ms, printf, "called every 1 seconds\n");

    // events are executed by the dispatch_forever method
    queue.dispatch_forever();
}

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.

/*
 * Copyright (c) 2020 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 */
#include "mbed_events.h"
#include <stdio.h>
using namespace std::chrono_literals;

/**
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.

Note the call() methods in this example only dispatch once as no period is
set and thus defaults to dispatch_once(). The ordering of the chaining dictates
the order of the dispatching.

Expected output:

hello from a!
hello from c!
hello from b!
**/

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_forever();
}

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).

/*
 * Copyright (c) 2020 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 */
#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", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

int main()
{
    // Request the shared queue
    EventQueue *queue = mbed_event_queue();
    printf("Starting in context %p\r\n", ThisThread::get_id());
    // 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();
}

Static EventQueue example: posting user allocated events to the static queue

Use static EventQueue to prevent your program from failing due to queue memory exhaustion or to prevent dynamic memory allocation:

/*
* Copyright (c) 2019 ARM Limited. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
* 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 "mbed.h"


// Creates static event queue
static EventQueue queue(0);

void handler(int count);

// Creates events for later bound
auto event1 = make_user_allocated_event(handler, 1);
auto event2 = make_user_allocated_event(handler, 2);

// Creates event bound to the specified event queue
auto event3 = queue.make_user_allocated_event(handler, 3);
auto event4 = queue.make_user_allocated_event(handler, 4);

void handler(int count)
{
    printf("UserAllocatedEvent = %d \n", count);
    return;
}

void post_events(void)
{
    // Single instance of user allocated event can be posted only once.
    // Event can be posted again if the previous dispatch has finished or event has been canceled.

    // bind & post
    event1.call_on(&queue);

    // event cannot be posted again until dispatched or canceled
    bool post_succeed = event1.try_call();
    assert(!post_succeed);

    queue.cancel(&event1);

    // try to post
    post_succeed = event1.try_call();
    assert(post_succeed);

    // bind & post
    post_succeed = event2.try_call_on(&queue);
    assert(post_succeed);

    // post
    event3.call();

    // post
    event4();
}

int main()
{
    printf("*** start ***\n");
    Thread event_thread;

    // The event can be manually configured for special timing requirements.
    // Timings are specified in milliseconds.
    // Starting delay - 100 msec
    // Delay between each event - 200msec
    event1.delay(100);
    event1.period(200);
    event2.delay(100);
    event2.period(200);
    event3.delay(100);
    event3.period(200);
    event4.delay(100);
    event4.period(200);

    event_thread.start(callback(post_events));

    // Posted events are dispatched in the context of the queue's dispatch function
    queue.dispatch_for(400ms);        // Dispatch time - 400msec
    // 400 msec - Only 2 set of events will be dispatched as period is 200 msec

    event_thread.join();
}

Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.