October 22, 2024
Chicago 12, Melborne City, USA
C#

FreeACT active object event synchronisation/safety


I’m thinking of writing an embedded application using the FreeACT framework to implement "Active Objects" event-driven system on a FreeRTOS system.

Fundamentally a FreeACT active object contains a FreeRTOS queue and a task. Events are posted to the active object, which unblocks the task to process the queue items in that tasks context (in a non-blocking way). So it’s non-blocking all the way down, and asynchronous back up.

However, something I do not understand is how the event are typically synchronised in such a system. This is the basic event type in FreeACT, it’s a single uint16:

typedef uint16_t Signal; /* event signal */


/* Event base class */
typedef struct {
    Signal sig; /* event signal */
    /* event parameters added in subclasses of Event */
} Event;

An event queue buffer looks like this, it’s statically allocated somewhere and given to the constructor:

    Event *active_queue[10];

So there are ten slots (say) for ten pointers to events, which is the working memory for a FreeRTOS queue.

Posting an event onto an Active Object’s queue is done with Active_post, which copies the pointer to the event onto the Active Object’s internal FreeRTOS queue (and there’s an from-ISR counterpart):

void Active_post(Active * const me, Event const * const e) {
    BaseType_t status = xQueueSendToBack(me->queue, (void *)&e, (TickType_t)0);
}

And the Active Object’s task (i.e. thread) will notice that and dispatch:

    for (;;) {   /* for-ever "superloop" */
        Event const *e; /* pointer to event object ("message") */

        /* wait for any event and receive it into object 'e' */
        xQueueReceive(me->queue, &e, portMAX_DELAY); /* BLOCKING! */
        configASSERT(e != (Event const *)0);

        /* dispatch event to the active object 'me' */
        (*me->dispatch)(me, e); /* NO BLOCKING! */
    }

And the dispatch function will get that event, and act:

static void BlinkyButton_dispatch(BlinkyButton * const me, Event const * const e) {
    switch (e->sig) {
        ...act!
    }
}

My question is: when you have posted your pointer to an event, how do you keep the event data safe until the Active Object 100% surely has acted on it?

For example, here is the blinky example from the FreeACt repo, where events come from a timer interrupt context:

void vApplicationTickHook(void) {
    ....
    if (press) {
        static Event const buttonPressedEvt = {BUTTON_PRESSED_SIG};
        Active_postFromISR(AO_blinkyButton, &buttonPressedEvt,
                           &xHigherPriorityTaskWoken);
    }
    ....
}

So there is a single static Event there, and the pointer to that will go off onto the Active Object’s queue, eventually (when the task awakens to deal with it) to be dequeued and handled.

But what is stopping vApplicationTickHook firing again and overwriting the event structure? The queue may be 10 items deep, but they’ll all be pointing to the same address.



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video