MessageBuffers for core-to-core communication

I am interested in using MessageBuffers as an inter-core message transport in an AMP setup, as described in Richard Barry’s blog post https://www.freertos.org/2020/02/simple-multicore-core-to-core-communication-using-freertos-message-buffers.html.

I can follow how this works, but I think there’s a problem if you want to use some MessageBuffers for inter-core communication (ICC), and some for communication within the same core (eg between an ISR and a Task on the same core). In this case you need to overload sbSEND_COMPLETED such that the ICC version gets used when it’s needed for the ICC-related buffers, but the standard implementation (in stream_buffer.c) gets used otherwise.

This doesn’t seem to be possible with the current mechanism for providing the custom implementation; as soon as we redefine sbSEND_COMPLETED we lose all access to the standard implementation, because its all opaquely hidden in stream_buffer.c.

Is this correct or have I misunderstood something?

Chris

Hmm - you raise a very good point, there is no easy way to do this. How would you like to see this made more flexible?

well, I’m not too familiar (yet) with the concept, but wouldn’t x[xxx]CreateBufferStatic() provide a straightforward solution? Since you know where the affected memory area is, you can keep a second reference to it and use that reference for the non-opaque processing (assuming of course that all concurrent accesses between the two domains of usage are appropriately serialized)?

I guess I’m a C++ programmer at heart. When we overload a virtual function to extend or modify the behaviour of a class, we always have access to the base class function, which we can call from within our overload function if that’s appropriate.

The current mechanism for overloading sbSEND_COMPLETED unfortunately implicitly removes access to the base class behaviour. It’s late at night now, so I’ll have a think tomorrow about how that access could be preserved without breaking the FreeRTOS coding style.

So, I’ve had a think about this. FreeRTOS doesn’t use virtual functions anywhere, so that doesn’t seem a wise direction to go, so how about a user-supplied ‘hook’ function that gets called at an appropriate point in xMessageBufferSend(), and whose return value can signal back whether to do the standard implementation of sbSEND_COMPLETED or not. Something like this:

xMessageBufferSend()
{
    /* If a time out is specified and there isn't enough
    space in the message buffer to send the data, then
    enter the blocked state to wait for more space. */
    if( time out != 0 )
    {
        while( there is insufficient space in the buffer &&
               not timed out waiting )
        {
            Enter the blocked state to wait for space in the buffer
        }
    }

    if( there is enough space in the buffer )
    {
        write data to buffer
#if configUSE_SB_COMPLETED_HOOK
        BaseType_t f = xSbCompletedHook(pxUpdatedBuffer) 
        if (f==pdTRUE)
        {
            sbSEND_COMPLETED(pxUpdatedBuffer)
        }
#else
        sbSEND_COMPLETED(pxUpdatedBuffer)
#endif
    }
}

I think this, possibly with equivalent changes in related functions, would satisfy what I need, but I recognise I’m not so familiar with the ‘big picture’ where FreeRTOS is concerned so it may be sub-optimal from some other angle.

EDIT: If done this way, maybe sbSEND_COMPLETED would no longer need to be a user-definable macro, but instead could be a regular private function or just inlined. Alternatively the macro could be retained, at the expense of complexity and potentially causing confusion, for backward compatibility (and maybe deprecated at some point).

The current method makes a ‘global’ change for all stream/message buffers. The alternative would be to add a callback parameter to the create buffer call that would allow a per-instance override of this operation (this would need to be a new function with the existing one just defaulting to the current behavior). This would add a ‘pointer’ to the size of the buffer control block, which isn’t that bad (and perhaps the addition of the pointer could be controlled by a macro like configUSE_SB_COMPLETED_CALLBACK so old code doesn’t even pay that cost.

Yes, that would work for me too. You call it a callback, I called it a virtual function, but it’s much the same thing - ie a function called through a pointer held in the per-instance StreamBuffer object.

One point to consider is whether there are valid use cases where you want to do something in addition to, as opposed to instead of, the default behaviour. I’m not knowledgeable enough about FreeRTOS’s ‘big picture’ to comment on that.

The difference between a callback and a virtual function is that virtual functions require that you have ‘classes’ with ‘inheretance’ which is beyond the capabilities of C. Call backs allow altering the behavior on a per-instance case, instead of having to create a ‘sub-class’ (which C doesn’t allow). Note that the current Queue/Semaphore/Mutex sharing of common ‘base’ code isn’t done with inheritance, but by carefully defining the Queue (the ‘base’) case as being generic enough that the others are just special cases of it, and the Queue code actually does handle at least most of the special case (I think recursive mutexes add special code, which is why you need to use a different call to use them).

See [Feature Request] Enable each stream buffer to use a different sbSEND_COMPLETED implementation · Issue #312 · FreeRTOS/FreeRTOS-Kernel · GitHub

Great. Thanks for your interest in this. In the mean time I have built a simple mechanism using cross-core interrupts and direct task notifications to synchronise a pair of threads, one on each core. I’ll use this to manage access to my own structure in shared memory from those two threads, rather than try to reuse the MessageBuffer from FreeRTOS.

I guess I’ll mark this thread ‘solved’, even though it’s more closed than solved!