Task Priority problem with Queues

Hi,
I am working on Queues. I am facing this problem with priority assignment of the tasks.
Problem statement
I have created two sender tasks (SenderTask1 and SenderTask2) and one receiver task. The code is working fine if the receiver task has higher priority and sendertask1 (normal priority) and sendertask2 has below normal priority.
This means I have assigned three different priorities for three tasks. This is all about working code and here is the code and its related output screen shot.

#include "main.h"
#include "cmsis_os.h"

UART_HandleTypeDef huart2;

osThreadId Sender1Handle;
osThreadId Sender2Handle;
osThreadId ReceiverHandle;
osMessageQId Queue1Handle;

int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

/* Configure the system clock */
	SystemClock_Config();
/* Initialize all configured peripherals */
	MX_GPIO_Init();
	MX_USART2_UART_Init();
	/* USER CODE BEGIN 2 */
	printf("Application start\r\n");

/* Create the thread(s) */
	/* definition and creation of Sender1 */
	osThreadDef(Sender1, StartSender1, osPriorityBelowNormal, 0, 512);
	Sender1Handle = osThreadCreate(osThread(Sender1), NULL);

	/* definition and creation of Sender2 */
	osThreadDef(Sender2, StartSender2, osPriorityNormal, 0, 512);
	Sender2Handle = osThreadCreate(osThread(Sender2), NULL);

	/* definition and creation of Receiver */
	osThreadDef(Receiver, StartReceiver, osPriorityAboveNormal, 0, 512);
	ReceiverHandle = osThreadCreate(osThread(Receiver), NULL);
      
       /* Create the queue(s) */
	/* definition and creation of Queue1 */
	osMessageQDef(Queue1, 256, uint8_t);
	Queue1Handle = osMessageCreate(osMessageQ(Queue1), NULL);

	/* Start scheduler */
	osKernelStart();
    
        while(1)
        {

        }
}


For Not working code

The only difference is priorities and here are the three lines that are not working. When the sender task1 and sender task 2 is having same priority what is happening?

/* Create the thread(s) */
	/* definition and creation of Sender1 */
	osThreadDef(Sender1, StartSender1, osPriorityNormal, 0, 512);
	Sender1Handle = osThreadCreate(osThread(Sender1), NULL);

	/* definition and creation of Sender2 */
	osThreadDef(Sender2, StartSender2, osPriorityNormal, 0, 512);
	Sender2Handle = osThreadCreate(osThread(Sender2), NULL);

	/* definition and creation of Receiver */
	osThreadDef(Receiver, StartReceiver, osPriorityAboveNormal, 0, 512);
	ReceiverHandle = osThreadCreate(osThread(Receiver), NULL);


Any one can discuss this problem and how to solve this issue. what is the problem with the priority. Can both sender tasks cannot have same priority and why the receiver task has to be highest priority than sender tasks.

You missed the important details about your FreeRTOS scheduling configuration (time sliced ? preemptive ?)
And following the selected scheduling scheme your expected behavior maybe including a short description of the tasks e.g. being busy loops or do they pause/block…
Did you already read some of the related docs like Mastering the FreeRTOS kernel ?
Please see ch. 3.12 Scheduling Algorithms

Hi, thanks for the reply. I am using preemptive scheduling algorithm. Yes and I have read the scheduling scheme

I would like to recommended using a tracing tool so you can see what’s going on. Such as Tracealyzer that we provide.

Based on my assumed definition of sender and receiver functions, in the first scenario, the following sequence happens:

  1. When the scheduler is started, the receiver task is the highest priority runnable task and it runs first.
  2. It blocks while trying to receive data from the queue and at this point the highest priority runnable task is sender2 which runs next.
  3. Sender 2 sends data to the queue and as a result, unblocks receiver task which runs next.
  4. Receiver task receives the value and then blocks again while trying to receive from the queue.
  5. Sender 2 is the highest priority runnable task and so it runs. It enters blocked state by calling delay.
  6. Sender 1 is now the highest priority runnable task and so it runs.

In the second scenario, at step 2 both sender 1 and 2 will be runnable and scheduler will pick one (Looking at your output, it seems that the Sender 1 is picked). Since sender 1 and 2 are at the same priority, they both should get CPU to run (it is based on the assumption that you are using preemptive scheduling as you mentioned). I am not sure why sender 2 is not running when the sender 1 enters the blocked state by calling delay.

Would you please share the definition of sender and receiver functions as well? Would be even better if you can share the complete project as I also want to look at how you enable printf output and if that is causing problem.

Thanks.

Hi,
Thanks for joining the conversation. As you said, why sender 2 is not running when the sender 1 enters the blocked state by calling delay. As I am using preemptive scheduling.

Here is the working code for two tasks sender1 (normal priority) and sender 2 (below normal priority) and single receiver task ( high priority ).

Please go through it. And let me know why the code will not work if sender1 (normal priority) and sender 2 also (normal priority) and single receiver task ( high priority ).

main.c (10.0 KB)
FreeRTOSConfig.h (5.7 KB)

Regards,
Kumar

You are using printf function in multiple tasks - printf is not thread safe and that is mostly the cause of the problem.

Please remove printf calls from Sender1 and Sender2 and let us know if that works.

Thanks.

1 Like

Hi,
Thanks for the reply.

By setting sender1 and sender2 set as normal priority and receiver as high priority. I have done this.
Experiment 1:
I think the problem is with the printf. I have removed the printf statements from sender1 and sender2. Its working fine.

Experiment 2:

I have set the osdelay() API in sender 1 (osdelay(1000)) and in sender 2 (osdelay(2000), without removing printf statements. This works fine and
getting failed if osdelay is less than 10ms

void StartSender1(void const * argument)
{

	/* USER CODE BEGIN 5 */
	/* Infinite loop */
	for(;;)
	{
		printf("Sender1 Sending\n");
		osMessagePut(Queue1Handle,0x1,200);
		printf("Sender1 Delay\n");
		osDelay(1000);
	}
	/* USER CODE END 5 */
}


void StartSender2(void const * argument)
{

	/* USER CODE BEGIN 5 */
	/* Infinite loop */
	for(;;)
	{
		printf("Sender2 Sending\n");
		osMessagePut(Queue1Handle,0x2,200);
 		printf("Sender2 Delay\n");
		osDelay(2000);
	}
	/* USER CODE END 5 */
}


/* USER CODE BEGIN Header_StartReceiver */
/**
 * @brief Function implementing the Receiver thread.
 * @param argument: Not used
 * @retval None
 */
/* USER CODE END Header_StartReceiver */
void StartReceiver(void const * argument)
{
	/* USER CODE BEGIN StartReceiver */
	osEvent retvalue;

	/* Infinite loop */
	for(;;)
	{

		retvalue=osMessageGet(Queue1Handle,2000);
		printf("Start Receiver Value : %d\n",(int)retvalue.value.p);
	}
 	/* USER CODE END StartReceiver */
}

So, now what may be the conclusion. Is it with printf()? statement or with osdelay() API for task switching. Why printf or osdelay is behaving like this. Can’t we use printf statements inside the tasks for debugging purpose. If not how to debug without using printf statements. Why printf is behaving like this

One clarification required. Is CMSIS version1 and version2 are different. If different, in what aspect they are? Please let me know.

Regards,
Kumar.

Your additional osdelay does what I call ‘synchronization by sleep’ which fundamentally flawed and must not be used.
Giving enough time to printf to complete before the other task kicks in makes it working … somehow.
The key here is ‘enough time’ which is basically unknown to the caller !
The root cause problem is, that you need to synchronize or better serialize the printf calls because usually printf is not thread-safe.
That means calls to printf from different tasks require e.g. an embracing mutex to ensure that only 1 task at a time executes the printf code. Otherwise things might break with varying unexpected behavior of your application.
So if you implement a printf wrapper function with e.g. a mutex-protected call to printf you should be fine.
There are lots of examples for logging functions out there which are often wrappers around standard (s)printf.
Or you find a printf implementation which is thread-safe.
Regarding the (OS) CMSIS versions you could ask the provider of your SDK. It’s not part of FreeRTOS.

The library printf may not be thread safe, and may use excessive CPU time spin waiting on the communications channel, possibly even with interrupts disabled.

I personally NEVER use printf in embedded code. I have my own serial output routines that I can send message through to track the program, that I know have been made thread safe, and have know blocking characteristics.

Can any one suggest a way to define printf in a thread safe manner. Can we use mutex or something for this? Is there a better way to do this?

A mutex around printf would help. One of the big issues is that often printf is configured to output via semi-hosting, and the semi-hosting output driver is often not Real-Time friendly (CPU Blocking, perhaps even with interrupts disabled).

The big issue is if you every look at printf, it can be a MASSIVE function, and much more than you need. I tend to use some very lightweight serial port drivers that I know what they do, so I don’t get surprises.

A common design pattern for prints is to have a spooler task, that handles the actual prints. Other tasks send the strings to this task via a queue. The xQueueSend calls can be encapsulated in wrapper function for thread-safe printing.

Can you share some specific example for this spooler task.

Hi Mr. John,
I am struggling to work with printf as serial console . My application have 3 tasks. 2 tasks will send the data to third task via queue and I want to print out that data on serial port.

Can you suggest any specific example template for that application.

Regards,
Kumar

How you write a serial driver, and how you direct printf() to a UART, are both very specific to the chip, compiler and libraries you are using. Does the chip you are using have a UART driver provided? If so, just use that directly to print text to the UART.