Creating event queue with event payload

I’d like to implement event-driven architecture. What I have at hand:

  1. Most logic is encapsulated in hierarchical state machine like [event_type, current_state, next_state, transition_function_pointer]. Entry, exit and transition functions can generate events that will be put into event queue to be handled later.

  2. In a low priority FreeRTOS task (just for convenience) I endlessly check if event queue is empty, if not - handleEvent(&fsm, &event) is called.

  3. I use vApplicationHook (I think that creation of a timer that fires once is a overkill) to implement an array of software timers, decrementing counters in the timers every tick. When a counter reaches zero, the corresponding event is put into event queue.

  4. ISRs generate events and put them into event queue.

It will be really good to make events to carry some variable payload, i.e. ADC value, pin state or UART Rx buffer. However, I do not know how to handle it correctly without lots of mutexes and possible dangling pointers.

For now events are defined as

typedef struct event {
uint8_t type; /* comes from enum */
void data; / different payloads for different event types */
} event_t;

and are created in-place and put in FreeRTOS queue by value

xQueueSend(xStructQueue, (void *) &((event_t){EventDummy, (void *)&(struct eventPayload){ local_val1, “dummy” }}), portMAX_DELAY);

What I am confused with:

  1. Even if enqueue by value, pointer to the payload will be dangling as event and payload are local variables (compound literals).

  2. How to pass the structures by reference in a proper way? As far as I know I should write data into some global buffer (array or circular buffer) to be sure the pointer will be persistent, protect read-write operations with mutex and critical section and choose the intermediate buffer to be big enough not to cause data overwrite.

Could you please recommend how likely tasks are handled? I’m sure I invented nothing new)


What if using dynamic allocation of event structs (e.g. using one of the FreeRTOS heaps like heap_3 or heap_4) and send receive pointers to them i.e. by reference ? That would limit shared access protection to just the heap implementation (where it’s already done correctly). Of course you could also implement your own custom (pool) allocator.
An possible generic event struct could consist of an event ID (enum), a size element specifying the size of an following variable sized array for event ID related payload.
The xQueueReceive API doc contains examples for using queues by value and by reference.

I just don’t like calling malloc in embedded systems. And I’m no sure that will not fragment heap - events are short-living and numerous. However, I never tried heap4, and don’t know if precautions are taken.

Queues and passing by value seem to be not a good choice since there could be need to send a couple of dozens bytes from ISR, and it can take relatively long. And I’d prefer an array or a list so that I can traverse it and insert to/ delete from middle.

If you can make the event structs fixed size you can add your own dedicated fixed block allocator. This is faster and avoids possible fragmentation of the main heap.
If you really want a lot more features (event lists) think twice if you really need it.
In practice a normal event input queue per task is often good enough. At least I’d use a FreeRTOS provided event transport mechanism and implement extra features (event buffering/lists) in task context.

Have you looked at MessageBuffers as a way to pass the events? That way you can pass the variable sized data by value an not need all the memory management.

Yes, I did. The problem is mainly related to the way how I declare event_t - if I pass an event by value to a queue memory location the data field points to can be overwritten while events wait to be processed.

That sounds like an ownership management issue. Sending the event by pointer just moves the issue from the data to the event itself. If you don’t know when you can reuse the data, how do you know when to reuse the event?

Thank you for your reply. Sometimes advice to stop and think really helps. Here is what I finished with on event topic

#define MAX_EVENTS 16
#define EVENT_PAYLOAD_SIZE sizeof(float)

typedef struct event {
	uint8_t type;
	void * data;
} event_t;

/* FreeRTOS queues are thread-safe, using them instead sync primitives */
QueueHandle_t 	pxStorageHolder = NULL;
QueueHandle_t 	xEvtQueue = NULL;

/* Before scheduler starts */
void * storageHolder = malloc(MAX_EVENTS * EVENT_PAYLOAD_SIZE);
pxStorageHolder = xQueueCreate(MAX_EVENTS, sizeof(void *));
for (size_t k=0; k < MAX_EVENTS; k++){
	/* Put pointers to payload to queue. We retrieve them at event creation */
	xQueueSend(pxStorageHolder, &(storageHolder + EVENT_PAYLOAD_SIZE*k), portMAX_DELAY);

/* When event is generated and sent */
uint32_t curr_adc_val = readADC();
void * tmp_data_ptr;
xQueueReceive(pxStorageHolder, &tmp_data_ptr, portMAX_DELAY);
event_t tmp_evt = {evtADCReady, tmp_data_ptr};
*((uint32_t *) = curr_adc_val;
xQueueSend(xEvtQueue, (void *) &tmp_evt, portMAX_DELAY);

/* When pointer is received and processed */
static event_t tmp_evt;
xQueueReceive(xEvtQueue, &tmp_evt, portMAX_DELAY);
/* Recycle memory */
xQueueSend(pxStorageHolder, &(, portMAX_DELAY);

Writing my own allocator at this case seems to be an overkill.

Also I gave up with idea of thread-safe lists. What I wanted to do: in addition to “handle as soon as possible” events there are ones delayed for a given time, so we should keep them for some and only then put into shared event queue; in terms of FreeRTOS it is timer with no auto reload, with constant callback function, but different period and parameters for callback function. To do so I wanted to use simple software timers implementation from here: (we.easyelectronics. ru/Soft/dispetcher-snova-dispetcher.html)(don’t mind language, I think the code is self-explanatory) instead of FreeRTOS timers with RTOS_DispatchTask in vApplicationTimerHook. I thought it would be cheaper in memory and processor ticks and not very hard to implement. I was wrong with the last part - I still don’t get how to make the list of timers thread-safe.

What I think to do now: Use FreeRTOS timers. Create an array of timers, when a delayed event is to be put into wait, search the timer array for inactive timer, set its period, supply the timer callback with event as parameter as shown here (freertos .org/FreeRTOS_Support_Forum_Archive/May_2016/freertos_xTimerCreate_vTimerCallback_b3906645j.html) and start the timer. It seems that no sync primitives should be used since timer data can be edited only from one place and only when timer is not active.

PS sorry for naked links, forum says that I’m to new to post links in a civilized way ;] If you’d like to follow them, remove space before dot.

Note, if all your events just have a little bit of data, that is where the Message Buffers can be useful. You create a set of DIFFERENT structures for different types of messages that need to pass different types of data. The first element is still the event code, which will always be first, and always the same type, with different values for different sorts of messages.

After that is then the actual data for that message, rather than a pointer to a small block of memory.

The senders know what type of message they are sending, and just create a structure of that type, fill in the data and send it. (one of the parameters for the send will be the size of the structure).

The receiver either uses a union of all the types to receive, or a properly aligned array and then take the address and cast that pointer to a pointer to the actual type of structure to access the data.

This way, you don’t need to worry about any memory management, as the structure is available again as soon as you send.

It does still sound like this one task getting on the data is a bit of a hodge lodge, and sounds like it doesn’t really have any real time constraints (as if it did, it would be hard to see if they are being met).

Thank you for the suggestion, it seems more FreeRTOS-ish and comfortable, I’ll definitely try it. The only thing I’m not comfortable with is that manual says that Message Buffers should have single sender and single reader, so for single handler I should have as many Message Buffers as there are senders and forget about unique event bus.

Yes, that is a limitation, but fairly easy to meet. You seem to have just a single receiver, so not a problem. Since ISRs are your sender, you just need to put each send inside an ISR critical section to enforce that requirement.

The requirement is that at any instant, there can be at most one sender and one receiver, not that a given buffer can only be sent by a single source or received by a single source. Part of the simplification of the Message Buffer is based on the assumption that it doesn’t need to deal with lists of users.

Thank you, thing “one at given time” wasn’t clear from manual. It makes everything much easier.