Question: Safe to send structures to a queue from multiple tasks without mutex?

bryce78 wrote on Thursday, October 13, 2016:

Hello FreeRTOS Community,

I recently started using FreeRTOS with STM32L471 Cortex M4 MCU. I’m having fun and I really like it. The documentation and manuals are well written – clear and easy to understand! :slight_smile: I would like to ask for a clarification, though, regarding the scenario of multiple tasks sending items to a queue. To be clear I give some details below.

I have a low-priority “diagnostic task” responsible for receiving messages from a “dignostic queue” and sending them via UART DMA. The messages contain pointers to strings in predefined buffers. That is, strings are not sent in the queue – just pointers to the strings.

Several other tasks (of higher and possibly equal priority) send messages to the “diagnostic queue” asynchronously. I use a mutex to ensure only one task is writing a string to a predefined buffer at a time. Once a pointer to the string is obtained I release the mutex and call xQueueSendToBack() to send the pointer to the “diagnostic queue”.

This appers to work well and I’ve not encountered any problems. My question is whether releasing the mutux before calling xQueueSendToBack() is safe, or that I should only do so after xQueueSendToBack() returns. Could someone clarify?

I’ve not seen any examples whereby several tasks sending messages to the same queue require a mutex obtain/release around xQueueSendToBack(). So, I think I’m okay. I just wanted to check with the experts.

Presumably there is already a mechanism that prevents more than one task from writing into the same queue at the same time?

Thanks for any clarification!
Bryce

heinbali01 wrote on Thursday, October 13, 2016:

Hi Bryce,

Just writing to a queue does not need a mutex, the write functions take care of that.

If you are sharing buffers among tasks, you will probably have to protect that.

Can’t you just pvPortMalloc() the space needed for the strings?

Regards.

edwards3 wrote on Thursday, October 13, 2016:

You do not need a mutex to protect accessing a queue, but if you are queuing pointers to anything you need to be careful nothing changes the data being pointed to before the receiving task has accessed it.

richard_damon wrote on Thursday, October 13, 2016:

If you are putting in the queue a pointer to a buffer, and the mutex is guarding that buffer, then you really need to hold the mutex until the task receiving the data has finished with the buffer. Putting a pointer on the queue has nothing to do with saving the contents that the pointer points to.

The queue will insure that a full stucture wil be copied as a unit, and no other task can intermix its data with this tasks data.

bryce78 wrote on Thursday, October 13, 2016:

Hello Hein, MEdwards, Richard:

Thank you for the quick responses and clarifications!

Yes, I used a mutex for protecting the shared memory from multiple tasks writing to it simultaneously. And I had to be careful that no other tasks accessed the memory until the receiver had finished with it. It was okay for a quick test…but definitely not robust.

I will follow Hein’s suggestion to malloc space for the strings, and then free it in the receiving task. With this approach I think I will not need mutexes at all. Is it correct that pvPortMalloc() protects against different tasks allocating the same memory?

Best,
Bryce

rtel wrote on Thursday, October 13, 2016:

pvPortMalloc() is just the FreeRTOS version of malloc() - basically
malloc() with some thread safety. It does not prevent more than one
task accessing the same memory - any task can access any memory it knows
the address of and has access to.

I think the suggestion was to malloc a memory buffer, copy the string to
the buffer, the post a pointer to the buffer on the queue. Then at the
other end, have the receiving task read the string out of the buffer,
then free the buffer again using vPortFree(). That way you don’t need a
mutex anywhere as only one task will be accessing the buffer and the
string contained in the buffer at any one time.

bryce78 wrote on Friday, October 14, 2016:

Thanks! That makes sense. :slight_smile:

I followed the suggestion as you described it, with one minor exception – I free the buffer (using vPortFree()) in the receving task only after the UART DMA transfer of the buffer indicates it is complete. I could free the buffer immediately if I copied it to a local array before starting the UART DMA transfer, but that seems unnecessary.

Thanks Community!