Single Task Setup with Task Notifications Fails to Execute Task at Highest Priority - Bug or how to fix?

tonofsteel wrote on Friday, May 13, 2016:

I am creating a task using the following:
xTaskCreate(GF_Task, “GF”, stackDepth, &taskParameter, GF_TASK_PRIORITY, &_taskHandle);

Inside of the task I am processing in a loop with a TaskNotifyWait:

    while (_taskRun)
		uint32_t ulInterruptStatus;
		xTaskNotifyWait(0x00, ULONG_MAX, &ulInterruptStatus, portMAX_DELAY);


Inside of the DMA half and full conversion complete ISR:

		if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
			BaseType_t xHigherPriorityTaskWoken = pdFALSE;
			xTaskNotifyFromISR(GF_Task_GetTaskHandle(), 2, eSetValueWithOverwrite, xHigherPriorityTaskWoken);
			portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);	// force a context switch if higher priority task woken

In main:


    /* Start scheduler */

When debugging I see there is a IDLE task, tmrsrv task and my “GF” task.

The issue is that the task will stop running pretty much as soon as the micro is started. It will just run the IDLE task continuously and never execute the GF task. The GF task is listed as being in pxReadyTasksLists. The idle task is priority 0, I think the tmrsvc was priority 2 and the GF task is priority 9. So how is it that FreeRTOS will execute a task with priority 0 and never execute my higher priority task which is in the ready list?

Going through the two xTaskNotify functions used above they look to be working correctly but something in FreeRTOS is causing a context switch to the IDLE task mid-stream. Since the systick will not trigger a pendSV unless a task times out waiting and the IDLE task will not PendSV unless there are other tasks with the same priority this system will lose its state.

If I use portEND_SWITCHING_ISR(pdTRUE); then this will trigger a context switch on every run of the ISR, but this shouldnt be required as the Notify function has this:

				if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
					/* The notified task has a priority above the currently
					executing task so a yield is required. */

So if it is executing the “GF” task it should continue and if it is in the IDLE task when the ISR is run it should then switch to the “GF” task. But this is only run: if( eOriginalNotifyState == eWaitingNotification )

When FreeRTOS is failing it is stuck in the eNotified state since the GF task never runs.

Shouldnt FreeRTOS be designed in such a way that this kind of issue could not happen? The above method of using Task Notifications was taken from the documentation but there is no explicit warning that the task will not always process after being notified, there is a chance it could go to the IDLE task and get stuck there unless you force a PendSV somehow. This should not even go the idle task in the first place if there is a notification to process.

Changing the code to always PendSV in the ISR is confusing and can easily be missed by someone, especially the way the code is structured and documented.

If this is what is somehow deemed the correct operation then these functions should be changed to something like xTaskNotifyFromISR_Sometimes_Maybe(); to indicate that the notification may not unblock a waiting task.

davedoors wrote on Saturday, May 14, 2016:

xTaskNotifyFromISR(GF_Task_GetTaskHandle(), 2, eSetValueWithOverwrite, xHigherPriorityTaskWoken);

should be

xTaskNotifyFromISR(GF_Task_GetTaskHandle(), 2, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);

tonofsteel wrote on Tuesday, May 17, 2016:

Gah! I am using half and complete DMA ISR’s and changed it in only one of them before asking the question!

Stupid error on my part, and didn’t even notice when I arrived at it again to copy/paste into here.

Thank you Dave for pointing this out