You are on the right track.
Blocking free exchange of fixed messages between threads / processes / processors 
Fixed-size ring buffers can be used for seamless communication between threads, processes, or processors if there is one manufacturer and one consumer. Some checks to perform:
The head variable is written only by the manufacturer (as an atomic action after writing)
the tail variable is written only by the consumer (as an atomic action after reading)
Trap: introducing a variable size or fill / empty buffer flag; they are usually written by both the manufacturer and the consumer, and therefore can cause problems.
I usually use ring buffers for this purpose. The most important lesson I learned is that a ring buffer cannot contain more elements. Thus, the variable head and tail are written by the producer and the consumer.
Extension for large / variable sized blocks. To use buffers in a real-time environment, you can use memory pools (often available in optimized form on real-time operating systems) or separate the distribution from use. The latter approaches the question, I think.

If you need to exchange large blocks, I suggest using a pool with buffer blocks and reporting pointers to buffers using a queue. So use the third line with buffer pointers. Thus, the distribution can be performed in the application (in the background), and your part in real time has access to a variable amount of memory.
request
while (blockQueue.full != true) { buf = allocate block of memory from heap or buffer pool msg = { .... , buf }; blockQueue.Put(msg) } Producer: pBuf = blockQueue.Get() pQueue.Put() Consumer if (pQueue.Empty == false) { msg=pQueue.Get() // use info in msg, with buf pointer // optionally indicate that buf is no longer used }
Adriaan
source share