VTaskDelay asserts when called from main()

moreshwar wrote on Tuesday, March 19, 2019:

Hello all,
I am using FreeRTOS. The board code (lower level) is autogenerated. This code calls vTaskDelay indirectly as it needs to wait for some milliseconds to allow for device initialization. The device initialization function is being called from main(). This is obvious as I need board to be initialized before creating tasks.
The device initalization function calls vTaskDelay() indirectly to delay further execution.
The device initialization function is unaware that no tasks exist when it is called.
vTaskDelay executes

/* We must remove ourselves from the ready list before adding
ourselves to the blocked list as the same list item is used for
both lists. */
if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )

Herein lies the problem
The comment says the task should remove itself from ready list
There is only one task at this stage i.e. main() . How will main remove itself?
the function
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
does not check validity of “pxItemToRemove->pxNext->pxPrevious” and “pxItemToRemove->pxPrevious->pxNext” before attempting the below assignments leading to crash.

pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

I agree that caller of vTaskDelay should have checked if there are any other tasks that can replace the current task. However, the basic rule says NULL pointer check is a must and neither vTaskDelay nor uxListRemove check that there is only one task and pointers can be NULL.

If I ask an OS to delay a task and there is no other task to replace the current task, a tight loop should be implemented in vTaskDelay or some other function provided.

There is no comment whatsoever that this function must be called if and only if there are more than one tasks and not in context of main() before any tasks are created.

It only states that “This function blocks (sleep) the current thread for a number of milliseconds.”.

I expected a comment saying that this function must not be called when this is only one task a for delay implement another function yourself if thread can’t be created.

It should be noted here that the idle task is created only when vTaskStartScheduler() and after vTaskStartScheduler(), control never returns to main() so doing any board level initalization after vTaskStartScheduler() in main() is impossible,

Can anyone confirm my finding and guide me on how to add delay in board level initialization in main() before i can create other tasks or call vTaskStartScheduler()?

richarddamon wrote on Tuesday, March 19, 2019:

vTaskDelay can only be called from a Task AFTER the sceduler has started. Note, the main function is NOT a task, tasks are only running after the scheduler started.

There are two possible solutions:

  1. use some function, either provided by the manufature or written by yourself that does a spin wait for the required time to implement the delay, or
  2. Start the scheduler before you need the delay, and do the initilization under FreeRTOS, and have the tasks that need that initilization done either not be recreated until after it is done, or be waiting for a signal that it is done.

I generally find that I can use the later, and often a given device is only needed by a single task, so the initilization code for that device can be just placed at the start of that task. If a device is used by multiple tasks, then I use a semaphore in that task to indicate that it is ready, and have a wait for it in the beginning of the API functions for it, and that gets set when the device finishes its initialization.

moreshwar wrote on Tuesday, March 19, 2019:

Hello Richard,
Thanks for reply.
I will have to use the second option as the device initialization code is autogenerated by the IDE and hence I cant modify the code with a tight loop (code will be removed in next auto generation).
However, I am not sure why

  1. vTaskDelay does not check if there is no task (main() is not a task) before attempting a deschedule.
  2. Why there is no check for NULL pointer anywhere in vTaskDelay or uxListRemove()? The code in uxListRemove causes an assertion. What are assumptions here? When the assertion is hit, there is no stack trace available to know where the crash occured and it took me little time to figure out that the assertion was coming from uxListRemove.

These two questions are for my understanding and help me in getting to know FreeRTOS better.

Thanks for your help.


richarddamon wrote on Tuesday, March 19, 2019:

One reason I can imagine for not adding a check is that for a properly written code, such a check just costs time. There are asserts in the code (and more are added in later versions) to catch most of these problems, and the assert in uxListRemove(), if you stopped the code in a debugger, it should have given you a stack trace back to the call of vTaskDelay being called by main.

As to vTaskDelay having a fall back for before the system starts, vTaskDelay is in portable code, and one thing about portable code is that it is very hard to write any form of accurate time delays. To provide such a function, then every port layer would need to have a spin wait loop for tick level time periods, which generally should not be needed in a well written piece of code, adding cost for small utility.