Hi, I am trying to write an "event dispatcher" so it's possible to listen to types of event and send events. You pass a function pointer and it will be called when that event is sent. Though I am having issues with unaligned memory access and I am unsure what is the cause.
Here is the code for the event dispatcher. I have removed some checks to simplify the code but essentially it's a 2d array of "listener". Each listener has a callback associated with it.
void eventDispatcher_task(void* context){
event_dispatcher_t* dispatcher = (event_dispatcher_t*)context;
while(true)
{
event_t event;
if(xQueueReceive(dispatcher->queue.handle, &event, portMAX_DELAY) == pdPASS){
// The enum value is it's idx in the array
uint16_t idx = event.type;
event_watchers_t* watcher = &dispatcher->watchers[idx];
// Send to all event listener of that type
for(uint16_t i=0; i < watcher->length; i++){
void* context = watcher->listeners[i].context;
watcher->listeners[i].callback(event->type, event->data, context);
}
}
}
}
// Create task and queue using static allocation
bool EventDispatcher_init(event_dispatcher_t* dispatcher) {
for(uint16_t i = 0; i<EVENT_TYPES_COUNT; i++){
dispatcher->watchers[i].length = 0;
}
dispatcher->queue.handle = xQueueCreateStatic(EVENT_LISTENER_QUEUE_SIZE,
sizeof(event_t),
dispatcher->queue.queueBuffer,
&dispatcher->queue.queueMem);
dispatcher->task.handle = xTaskCreateStatic(
eventDispatcher_task,
"evt_dispatch",
EVENT_LISTENER_STACK_SIZE,
dispatcher,
EVENT_LISTENER_PRIORITY,
dispatcher->task.taskStack,
&dispatcher->task.taskBuffer);
return true;
}
bool EventDispatcher_registerListener(event_dispatcher_t* dispatcher, event_type_t type, event_callback_t callback, void* context){
uint16_t idx = type;
event_watchers_t* watcher = &dispatcher->watchers[idx];
// Add listener to the list and increment it's length
watcher->listeners[watcher->length].callback = callback;
watcher->listeners[watcher->length].context = context;
watcher->length++;
return true;
}
bool EventDispatcher_dispatch(event_dispatcher_t* dispatcher, event_type_t type, event_data_t data) {
event_t event;
event.type = type;
return xQueueSend(dispatcher->queue.handle, &event, TIME_MS_TO_RTOS_TICK(10)) == pdPASS;
}And the .h with the types definitions
#include "stdint.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#define MAX_EVENT_LISTENER (16)
#define EVENT_LISTENER_QUEUE_SIZE (16)
#define EVENT_LISTENER_STACK_SIZE (512)
#define EVENT_LISTENER_PRIORITY (3)
/**
* @breif The types of event
*/
typedef enum {
EVENT_ALL_EVENT, /** @brief Listen to all event */
EVENT_START_SESSION, /** @brief Listen to start session events */
EVENT_TYPES_COUNT, /** @brief The number of event types */
} event_type_t;
/**
* @brief The data associated with the events
* */
typedef union {
struct {
uint8_t test;
} start_session;
} event_data_t;
/** @brief The struct containing the event and the data*/
typedef struct {
event_type_t type; // The event type
event_data_t data; // The data linked to the event
} event_t;
/** @brief Type definition for the callback of the event listener
* @param event_type The type of the event
* @param data The data of the event
* @param context The context passed at the registration of the callback
*/
typedef void (*event_callback_t)(event_type_t event_type, event_data_t data, void* context);
/** @brief The event listener storing information for the callback */
typedef struct {
event_callback_t callback; /** @brief The callback to call */
void* context; /** @brief The context to pass to the callback */
} event_listener_t;
/** @brief An array of event listener associated with a certain type */
typedef struct {
event_listener_t listeners[MAX_EVENT_LISTENER]; // The listeners
uint32_t length; // Length of the listeners
} event_watchers_t;
/** @brief queue structure for static allocation */
typedef struct {
QueueHandle_t handle;
StaticQueue_t queueMem;
uint8_t queueBuffer[EVENT_LISTENER_QUEUE_SIZE];
} event_dispatcher_queue_t;
/** @brief task structure for static allocation */
typedef struct {
TaskHandle_t handle;
StaticTask_t taskBuffer;
StackType_t taskStack[EVENT_LISTENER_STACK_SIZE];
} event_dispatcher_task_t;
typedef struct {
event_watchers_t watchers[EVENT_TYPES_COUNT]; // One watcher for each event type
event_dispatcher_task_t task; // Static task allocation
event_dispatcher_queue_t queue; // STatic queue allocation
} event_dispatcher_t;
/** @brief Initialize the dispatcher
* @param dispatcher The dispatcher to initialize
* @return If the operation was successful or not */
bool EventDispatcher_init(event_dispatcher_t* dispatcher);
/** @brief Register a listener to the dispatcher
* @param dispatcher The dispatcher to register to
* @param type The type of event to listen to
* @param callback The callback to call on the event
* @param context The context passed to the callback on event
* @return True if the operation was successful, false if not
*/
bool EventDispatcher_registerListener(event_dispatcher_t* dispatcher, event_type_t type, event_callback_t callback, void* context);
/** @brief Dispatch an event to the listeners
* @param type The type of event to send
* @param data The data of the event
* @return True if the operation was successful, false if not
*/
bool EventDispatcher_dispatch(event_dispatcher_t* dispatcher, event_type_t type, event_data_t data);Here is where the code is used.
void test_callback(event_type_t event_type, event_data_t data, void* context){
uint8_t* val = context;
(*val)++;
LOG_INFO("Event %d with data %d, context %d", event_type, data.start_session.test, *val);
}
event_dispatcher_t event_dispatcher;
int main_freertos(void)
{
// ... Some initialisation code
// Called after freertos is initialized and the scheduler is running
EventDispatcher_init(&event_dispatcher);
static uint8_t counter = 0;
// Register listener
EventDispatcher_registerListener(&event_dispatcher, EVENT_START_SESSION, test_callback, &counter);
// Send event, the callback should be called everytime
while(true){
EventDispatcher_dispatch(&event_dispatcher, EVENT_START_SESSION, (event_data_t){.start_session.test = 42});
vTaskDelay(10);
}
}The event is trigger three to four times and after a hard fault stops the application. Here is the message displayed by the hard fault handler.
// Initialization logs.... <info> app: [main.c:577] Event 1 with data 42, context 1 <info> app: [main.c:577] Event 1 with data 42, context 2 <info> app: [main.c:577] Event 1 with data 42, context 3 <info> app: [main.c:577] Event 1 with data 42, context 2 <error> hardfault: HARD FAULT at 0x0002A86A <error> hardfault: R0: 0x00000013 R1: 0x20008FF4 R2: 0x20008FF8 R3: 0x200098A0 <error> hardfault: R12: 0x20011008 LR: 0x0002A85D PSR: 0x6100000E <error> hardfault: Cause: The processor has made an unaligned memory access.
I tried aligned my struct using
__attribute__ ((aligned (4)))
on the typedef but it didn't fix the issue since I though this was the issue.
I am using Segger Embedded Studio 4.5 on Windows 10
SDK 15.3.0 on the nrf52840
I know this is a lot of information at once, but since don't know where the alignment issue can come from and no stack trace is available, I've tried to provide all the necessary info.
I don't know if it's caused by the static freertos stack allocation or when the even triggers.
Any help is appreciated and I am more than happy to provide more info if necessary
UPDATE: It seems even when not listening to an event, the issue is present. Thus the issue is necessarily in `eventDispatcher_task`, `EventDispatcher_dispatch` (though not much is done here) or `main_freertos` and the callbacks are not a problem