[freeRTOS Development Notes] Why is the xTaskCreate interface stuck and does not return

Keywords: FreeRTOS

1 Preface

Recently, bloggers are doing some projects to adapt freeRTOS. In short, they are migrating from other RTOS platforms to freeRTOS platform.
Since the previous codes are available, based on experience, we think that we only need to re encapsulate the interface of OSAL, and the upper logic should not be a problem in theory; But what we didn't expect is that we encountered some problems that we didn't consider before during the adaptation of OSAL layer.

2 problems encountered

Briefly describe the problems I encountered; This problem is mainly reflected in the interface call of creating task xtask create. The interface prototype of freeRTOS is:

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName,     /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION;

I call this interface in a task to create a new task, and then I find that the new task runs, but the task I initiated does not run down. The preliminary observation is that the xTaskCreate interface does not return.

3 problem analysis

3.1 preliminary analysis

Considering that my operation scenario is to migrate code from other RTOS platforms to freeRTOS platform, the first feeling is whether there is a problem with the OSAL layer encapsulation?
Therefore, the task creation related OSAL interface was re smoothed, including whether the parameter transfer conversion of each parameter was correct, and it was confirmed that there was no problem with the encapsulation.
When we did code debugging before, we left a hand, that is, when we created a task in xTaskCreate, we would print out some information related to the task for observation.

					/* Allocate space for the TCB. */
                    pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */

                    if( pxNewTCB != NULL )
                    {
                        /* Store the stack location in the TCB. */
                        pxNewTCB->pxStack = pxStack;
                        kprintf("[THD]%s:[tcb]%x [stack]%x-%x:%d:%d\r\n", pcName, 
                        pxNewTCB, pxStack,
                        (size_t)pxStack + ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), 
                        ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) )
                        , uxPriority);
                    }
                    else
                    {
                        /* The stack cannot be used as the TCB was not created.  Free
                         * it again. */
                        vPortFreeStack( pxStack );
                    }

So this debugging information gave me a clue. I found the following log in the place where the call creation task is stuck and does not return:

[THD]cli:[tcb]41a360 [stack]419758-41a358:3072:60

Task priority is 60? It seems that freeRTOS does not support the priority of such a high value?
So far, it is preliminarily suspected that it is caused by the problem of task priority value.

3.2 deep study of source code

So step by step to delve into the source code of freerTOS, which is the benefit of open source code!
The key path of code call is outlined:

xTaskCreate ->
prvInitialiseNewTask ->
prvAddNewTaskToReadyList ->

There are the following code snippets in prvInitialiseNewTask:

/* This is used as an array index so must ensure it's not too large. */
    configASSERT( uxPriority < configMAX_PRIORITIES );
    if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
    {
        uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    pxNewTCB->uxPriority = uxPriority;

This code is mainly used to check and process the task priority value. Here you can see that it will follow configmax_ For example, in my environment, the value is 10, which is defined in freeRTOSConfig.h.
From this code, we can know that the priority of 60 passed in by the application layer has actually been modified to 9; In freeRTOS, this is the highest priority.

/* Task */
#define configMAX_PRIORITIES		                ( 10 )

In freeRTOS, this value can be defined larger, but it needs to consume more RAM.

Then there is the following code snippet in prvAddNewTaskToReadyList:

    if( xSchedulerRunning != pdFALSE )
    {
        /* If the created task is of a higher priority than the current task
         * then it should run now. */
        if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
        {
            taskYIELD_IF_USING_PREEMPTION();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

Pay special attention to the second if statement. If the new task created has a higher priority, execute it immediately:

#define portYIELD()					__asm ( "SWI 0" )

#ifndef portYIELD_WITHIN_API
    #define portYIELD_WITHIN_API    portYIELD
#endif

#if ( configUSE_PREEMPTION == 0 )

/* If the cooperative scheduler is being used then a yield should not be
 * performed just because a higher priority task has been woken. */
    #define taskYIELD_IF_USING_PREEMPTION()
#else
    #define taskYIELD_IF_USING_PREEMPTION()    portYIELD_WITHIN_API()
#endif

On my platform, I directly ran to SWI 0 and generated a soft interrupt, and then task scheduling occurred immediately.
At this time, the low priority task (the task that calls xTaskCreate) feels unable to execute further, because the log is not typed!

3.3 code verification

In fact, through the debug information, I only need to add debugging information to the entry and exit of xTaskCreate to know whether to exit or not, but I can't verify whether it is true: high priority tasks can exit when creating low priority tasks, while low priority tasks can exit when creating high priority tasks.
So I wrote the following code for a simple verification:

#include "task.h"

TaskHandle_t calling_task;
TaskHandle_t lower_task;
TaskHandle_t higher_task;

void create_lower_task(void *data)
{
    TaskStatus_t TaskStatus;

    vTaskGetInfo( lower_task,
                   &TaskStatus,
                   0,
                   eInvalid);
    while(1) {
        task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
        vTaskDelay(1000);
    }
}

void create_higher_task(void *data)
{
    TaskStatus_t TaskStatus;

    vTaskGetInfo( higher_task,
                   &TaskStatus,
                   0,
                   eInvalid);
    
    while(1) {
        task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
        vTaskDelay(1000);
    }
}

void create_calling_task(void *data)
{
    int ret;
    int lower_prio = 3;
    int higher_prio = 60; //final set to prio 9
    TaskStatus_t TaskStatus;

    (void)higher_prio;

    vTaskGetInfo( lower_task,
                   &TaskStatus,
                   0,
                   eInvalid);

    task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
    ret = xTaskCreate(create_lower_task, "test-2", 256, NULL, lower_prio, 
        (TaskHandle_t * const )&lower_task);
    if (ret != pdPASS) {
        task_debug("Error: Failed to create test task: %d\r\n", ret);
    }
    task_debug("%s:%d >>>\r\n", __func__, __LINE__);
#if 1
    task_debug("%s:%d >>>\r\n", __func__, __LINE__);
    ret = xTaskCreate(create_higher_task, "test-3", 256, NULL, higher_prio, 
        (TaskHandle_t * const )&higher_task);
    if (ret != pdPASS) {
        task_debug("Error: Failed to create test task: %d\r\n", ret);
    }
    task_debug("%s:%d >>>\r\n", __func__, __LINE__);
#endif

    while(1) {
        task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
        vTaskDelay(1000);
    }
}

void freertos_task_priority_test(void)
{
    int ret;
    int cur_prio = 4;

    {
        TaskStatus_t TaskStatus;

        vTaskGetInfo( xTaskGetHandle( "extended_app" ),
                   &TaskStatus,
                   0,
                   eInvalid);
        task_debug("%s:%d >>> prio: %d\r\n", __func__, __LINE__, TaskStatus.uxCurrentPriority);
    }

    task_debug("%s:%d >>>\r\n", __func__, __LINE__);
    ret = xTaskCreate(create_calling_task, "test-1", 256, NULL, cur_prio, 
        (TaskHandle_t * const )&calling_task);
    if (ret != pdPASS) {
        cli_printf("Error: Failed to create test task: %d\r\n", ret);
    }
    task_debug("%s:%d >>>\r\n", __func__, __LINE__);
}

#endif

As soon as the code ran, it exceeded my expectation:

freertos_task_priority_test:793 >>> prio: 4
freertos_task_priority_test:796 >>>
[THD]test-1:[tcb]419b68 [stack]419760-419b60:1024:4
freercreate_calling_task:758tos_task_priority_test: >>> prio: 4
[THD]test802 >>>
[THD]cli:[tcb]-2:[tcb]41a1e8 [stack]4419bd8 [stack]41a258-4119de0-41a1e0:1024:3
creae58:3072:60
ate_calling_task:764 >>>
create_calling_task:766 >>>
[THD]test-3:[tcb]419c48 [stack]41ae60-41b260:1024:60
create_higher_task:739 >>> prio: 9
create_calling_task:772 >>>
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3

create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3
create_higher_task:739 >>> prio: 9
create_calling_task:776 >>> prio: 4
create_lower_task:724 >>> prio: 3

In short, xTaskCreate is not stuck at all?
For a task with task priority of 4, create tasks with task priority of 3 and 9 respectively. They all run well. No problem is found that xtask create does not return!
Is there something wrong with me?

3.4 further analysis

The above simple code has verified that my previous conjecture is wrong, but I do see that xtask create is stuck and does not return in my application code. I still need to analyze the newly created task in detail. Most of the problems are caused by it. Because I blocked the creation of it, the problems do not recur.
In order to illustrate the problem, I have simplified the execution code of this task as follows:

void task_main(void *data)
{
    int32_t ret;

    char *msg = NULL;

    while (!task_cancel_check()) {
        if (task_get_input(g_cli->inbuf, &g_cli->bp) != 0) {
        	/* do something */
        }
    }

    task_exit();
}

When you see the pseudo code, you may find some problems. The while looks like the code under query, and there is no delay processing. If the priority of this task is the highest, it will always occupy the CPU, and other tasks cannot be scheduled at all.
Recalling my code scenario, I passed in a priority of 60, which was reduced to configMAX_PRIORITIES-1, i.e. priority is 9; This is the highest priority task in freeRTOS, so xTaskCreate cannot exit and return; Because at this time, except for the task with the highest priority, other tasks may not run.

3.5 how to optimize

After understanding the cause of the problem, it is easy to modify it. Here are two ideas:

  1. Put this task_main is modified to an appropriate priority. For this non urgent task, it is recommended to set it as a secondary priority, that is, the priority value is 1;
    2) In task_ In the while loop of main, add an appropriate delay, such as vtask delay (1), to give up the CPU at the appropriate time.

Theoretically, the above two solutions can solve the problem, but mode 1 is definitely strongly recommended because it is the way to solve the root cause.

4. Experience summary

  • freeRTOS's priority definition is different from others. Don't confuse it!
  • When creating tasks in freeRTOS, pay attention to the priority. You can't define the priority at will!
  • For high priority tasks, it is possible to create low priority tasks; But conversely, it is also possible to create high priority tasks in low priority tasks!
  • Write the execution function of the task. Be careful not to let it run. If you do not call the blocking interface that can cause the system to hang, properly use the vtask delay interface to make the task give up the CPU.

5 more sharing

Welcome to my github warehouse 01workstation , daily share some development notes and project practice, welcome to correct the problem.

You are also welcome to follow my CSDN home page and column:

[http://yyds.recan-li.cn]

[C/C + + language programming column]

[GCC column]

[information security column]

[RT thread Development Notes]

[freeRTOS Development Notes]

If you have any questions, you can discuss with me and answer them. Thank you.

Posted by dk44 on Tue, 16 Nov 2021 07:06:46 -0800