FreeRTOS program stuck when vTaskDelete called from function

Thread Starter

zazas321

Joined Nov 29, 2015
936
Hello. I am playing with FreeRTOS and classes and found an issue. All I am trying to do is to start task1, count to 10 and call my custom method
State_change_handle which is going to delete the current task and start a new one. However, the issue is that the program hangs after the vTaskDelete is called. My cpp file:
Code:
#include "Controller.h"
Controller::Controller(){
    printf("Thermostat object created \n");
}
void Controller::begin(){
    this->test1 = 1;
    this->old_state = INITIAL;
    this->new_state = INITIAL;
    this->main_task_handle = NULL;
    this->secondary_task_handle = NULL;
    printf("Start the task1 \n");
    this->State_change_handle(TASK1);
    //xTaskCreate(task3,"TASK3",10000,(void*)this,1,&this->secondary_task_handle); // receiving commands from main uart
}
void Controller::State_change_handle(e_thermostat_state state)
{
    this-> new_state = state;
    if(this-> old_state != this-> new_state ){ // if the new state is different than current state, delete the task
        if (this ->main_task_handle != NULL){
            printf("different state, delete the previous task \n");
            printf(" deleting task with name = %s \n",pcTaskGetTaskName(this->main_task_handle));
            vTaskDelete(this->main_task_handle);
            printf("task deleted sucesfully \n"); // this never prints for some reason
        }
        else{
            printf("no tasks currently running \n");
        }
    }
    else
    {
        printf("same task \n"); // the same task is set, dont do anything
        return;
    }
        
    switch(this->new_state) 
    {   
        case TASK1:
            printf("NEW STATE = TASK1 \n");
            this-> old_state = TASK1;
            xTaskCreate(task1,"TASK1",10000,(void*)this,1,&this->main_task_handle); // receiving commands from main uart
            break;
        case TASK2:
            printf("NEW STATE = MODE2\n");
            this-> old_state = TASK2;
            xTaskCreate(task2,"TASK2",10000,(void*)this,1,&this->main_task_handle); // receiving commands from main uart
            break;
        default:
            printf("state not recognised \n");
    }
}
my .h file:
Code:
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "stdint.h"
#include "stdio.h"
//#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//#include "esp_system.h"
//#include "esp_log.h"
//const char* TAG1 ="RTOS";
enum e_thermostat_state
{
    INITIAL,
    TASK1,
    TASK2,
    TASK3,
    MAX_STATES
};

class Controller
{
    private://only accesible for class
        int test1;
        e_thermostat_state new_state;
        e_thermostat_state old_state;
        TaskHandle_t main_task_handle;
        TaskHandle_t secondary_task_handle;
        
    public: 
    //accesible outside class
        Controller();                                                   // INIT OBJECT
        void begin();
        void State_change_handle(e_thermostat_state state);

        static void task1(void* parameters)
        {
            Controller* controller = (Controller*)parameters;         
            for(;;)
            {   
                controller->test1++;
                printf("hello from task 1\n");
                if(controller->test1 % 10 == 0){
                        printf("changing to TASK2\n");
                        controller->test1 = 1;
                        controller->State_change_handle(TASK2);
                }
                vTaskDelay(1000/portTICK_RATE_MS);
            }
        }

        static void task2(void* parameters)
        {
            
            Controller* controller = (Controller*)parameters;
            for(;;)
            {   
                printf("hello from task 2\n");
                controller->test1 ++;   
                if(controller->test1 % 5==0){
                    printf("changing to TASK1\n");
                    controller->test1 = 1;
                    controller->State_change_handle(TASK1);
                }
                vTaskDelay(2000/portTICK_RATE_MS);
            }
        }
        static void task3(void* parameters)
        {
            Controller* controller = (Controller*)parameters;         
            for(;;)
            {   
                printf("hello from task 3\n");
                vTaskDelay(3000/portTICK_RATE_MS);
            }
        }

        //static void normal_operation_task(void *argument);                                 // DECLARE DEFAULT THERMOSTAT STATES AND VARS       
};



#endif
In my main.c I just create class object and call begin method. Then the task1 will start. The serial monitor:
Code:
Start the task1
no tasks currently running
NEW STATE = TASK1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
changing to TASK2
different state, delete the previous task 
 deleting task with name = TASK1
As you can see from the serial monitor, nothing else happens after vTaskDelete. Could someone help me understand what is the issue?
 

mckenney

Joined Nov 10, 2018
125
I haven't tried this, but I wonder what you expect to happen when your task deletes itself. It would be unsurprising for the task not to do anything else, since it has been deleted.

Looking at the code in tasks.c, vTaskDelete removes the TCB from the list of tasks, then Yields. Since it's not in the task list it won't be rescheduled.

Maybe you intended to create the new task before deleting the current one?
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
Thanks for the response. After playing around I think I understand what is the issue but I am not sure.

If I call state_change_handle function from outside the task, then it works without any issues. The problem only happens when the state_change_handle is called from within the task itself which causes the task to delete itself before starting a new one. What I can do instead:

Code:
static void task2(void* parameters)
        {
            
            Controller* controller = (Controller*)parameters;
            for(;;)
            {   
                printf("hello from task 2\n");
                controller->test1 ++;   
                if(controller->test1 % 5==0){
                    printf("changing to TASK1\n");
                    controller->test1 = 1;
                    //controller->State_change_handle(TASK1);
               
                    xTaskCreate(TASK1,"TASK1",10000,controller,1,&controller->xHandle); 
                    vTaskDelete(0);

                }
                vTaskDelay(2000/portTICK_RATE_MS);
            }
        }
The above works fine, however, it defeats the whole purpose of my state_change_handle method which I have written to handle all the tasks being created/deleted
 

Irving

Joined Jan 30, 2016
5,002
Whats the reasoning for one task to create another before killing itself? seems unnecessarily complicated...
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
Because I have multiple "main_tasks" and multiple "secondary_tasks". Only one "main_task" can run at once. Which task will be running will depend on my controller status. So for example some pseudo code:
Code:
if(controller == ACTIVE) {
   delete_any_other_main_task_if_running();
   start__main_task_1();
}


else if(controller == PASSIVE) {
delete_any_other_main_task_if_running();
start_main_task_2();
}
This is my current idea. I havent yet found the best way to manage the tasks. Perhaps I should have a seperate RTOS task that only handles task changing/deleting/suspeding and creating
 

Irving

Joined Jan 30, 2016
5,002
This is my current idea. I havent yet found the best way to manage the tasks. Perhaps I should have a seperate RTOS task that only handles task changing/deleting/suspeding and creating
I think an overarching supervisory task could be a good solution to this. The concept of more than one 'main' task is.... paradoxical?

Or have all 'main' tasks running but blocked on a semaphore and then release one or other semaphore to let the relevant 'main' task run.

Or have one main task running two or more parallel state machines...
 
Last edited:
Top