Application stucks on "taskSELECT_HIGHEST PRIORITY_TASK" because get invalid ready list

matheus-pinto wrote on Monday, November 18, 2019:

Hello,

I am developing a application using MK22FN512VLH12, an ARM Cortex-M4 microcontroller, using NXP MCUXpresso IDE and its FreeRTOS v.9.0.0 port available.
The application begins with a highest priority management task (“ManTask”) that creates another five tasks (“Task1”, “Task2”, …, “Task5”). It begins with Task5 with highest priority and follow with Task1 with lower priority between this five tasks. Each of this tasks must execute in a periodic manner using “vTaskDelayUntil” FreeRTOS API function.
After start the five tasks, ManTask is delayed for a predetermined period, so the five tasks can execute. However, the follow behavior happens:

  • Task 5 begins its execution and is blocked by "vTaskDelayUntil;
  • After that Task 4 is wake up and is blocked too by "vTaskDelayUntil;
  • Then, the application gets stuck on taskSELECT_HIGHEST PRIORITY_TASK macro in “vTaskSwitchContext”.

I know that application is stuck in this part of code when no task is available for scheduler. I know too that IDLE task must always be available, and it is initialized on scheduler start.
For debug application, I am using SEGGER SystemView. I put a SEGGER printf log inside taskSELECT_HIGHEST PRIORITY_TASK macro to see the lenght of ready tasks lists and the list chosen:

#define taskSELECT_HIGHEST_PRIORITY_TASK()	\
	{	\
	                UBaseType_t uxTopPriority;	\
		/* Find the highest priority list that contains ready tasks. */		\
		portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );	\
		for(int i = 0; i < configMAX_PRIORITIES; ++i) SEGGER_SYSVIEW_PrintfTarget("List %d Size: %d\n",i, listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ i ] ) ));\
		SEGGER_SYSVIEW_PrintfTarget("uxTopPriority: %d\n", uxTopPriority);\
        SEGGER_SYSVIEW_PrintfTarget("\n");\
		configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 );		\
		listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );		\
	}

As can be saw in attached figure, the behavior is what is expected until Task5 be blocked with “vTaskDelayUntil”. After that Task4 is called, execute its code and call “vTaskDelayUntil” too. But although Task4 has left its ready tasks list, its corresponding list is chosen in portGET_HIGHEST_PRIORITY (uxTopPriority = 5). Because there are no tasks ready for execution in this list, the scheduler assumes that there are no tasks ready for execution.

I have no idea why this behavior. Any hints?

Thanks,

Matheus

rtel wrote on Monday, November 18, 2019:

First I would recommend taking the calls to
SEGGER_SYSVIEW_PrintfTarget() out from that part of the code - it
executes very frequently and from both interrupts and tasks - adding
such a delay (and potential stack usage) into the code there will
completely change the behaviour that you are trying to observe.

Can you please elaborate on “> - Then, the application gets stuck on
|taskSELECT_HIGHEST PRIORITY_TASK|” What do you mean by ‘stuck’? Is it
hitting the assert()? If you break on the debugger which code is running?

matheus-pinto wrote on Tuesday, November 19, 2019:

Thanks for reply Richard,

Answering your questions, the code is stopped in the taskSELECT_HIGHEST PRIORITY_TASK macro when debugged. So it probably hits assert(). What I mean by ‘stuck’ is to hitting assert(). When I took SEGGER_SYSVIEW_PrintfTarget() from vTaskSwitchContext the program still stopped at taskSELECT_HIGHEST PRIORITY_TASK.

Attached is a screenshot of the system behavior in SEGGER SystemView, when the function SEGGER_SYSVIEW_PrintfTarget() is removed.

rtel wrote on Tuesday, November 19, 2019:

If it is hitting that assert then it is selecting a priority at which
there are no tasks ready to run - that would indicate a memory
corruption somewhere - and the most probably cause of that would be an
incorrect interrupt priority assignment. I would recommend ensuring you
are using a very recent version of FreeRTOS (as that contains a lot more
asserts to check interrupt priorities) and then run the code still with
configASSERT() defined. That might catch something.

matheus-pinto wrote on Tuesday, November 19, 2019:

I don’t think this is an interrupt issue, because I’m not using any. It is also worth mentioning that tasks are encapsulated in C ++ objects. All objects are created with pvPortMalloc() and their pointers ("this") are passed as arguments to the corresponding task in order to access their methods.

In fact, the objects are created like that:

Task *t;
void *p = pvPortMalloc(sizeof(Task));
t = new(p) Task().

And inside Task object, the tasks are created like that:

xTaskCreate(this->func, this->name, this->memory, (void*)this, this->priority, &(this->handle));

rtel wrote on Tuesday, November 19, 2019:

It
is also worth mentioning that tasks are encapsulated in C ++ objects.

Vital information missing from your original post - much better to have
C++ in the message subject to folks that use C++ see the post.

matheus-pinto wrote on Wednesday, November 20, 2019:

I solved the problem. This was a classic problem of low memory size for task stacks. I kept increasing the size of the stacks until at some point the program operated as expected. The tasks workloads was based from here: https://github.com/SavchenkoValeriy/wazuhl-llvm-test-suite/blob/master/SingleSource/Benchmarks/Misc/whetstone.c. The created macro code of tasks workload is attached.

I believe the variables created in the PA, P0, and P3 functions caused the stacks to overflow.

richard_damon wrote on Wednesday, November 20, 2019:

One good reason to enable stack overflow checking by default in a new project and disable it later when you know stack sizes are good enough if you need more performance.

One other note, I would be a bit leary of your method of using C++, you apparently are using placement new because you are afraid that the build in new isn’t going to be thread safe, but you then need to be VERY careful of what parts of the language/library you use to make sure that nothing you use will actually invoke the unsafe memory management, at the minimum you will need to periodically review the linker map to verify that malloc/new (other than your placement new) are never called, or trace back the calls to verify they are all right. It is much safer (but more up front work) to look into what is needed to make the library memory management thread safe.