freertos task does not cleanup c++ class objects on vTaskDelete

chegewara
Posts: 2364
Joined: Wed Jun 14, 2017 9:00 pm

freertos task does not cleanup c++ class objects on vTaskDelete

Postby chegewara » Tue Feb 13, 2024 1:43 am

Hi,
just recently i found a strange issue and im not sure what is the problem:
- freertos,
- esp-idf,
- compiler?

Description:
- when using C++ class local objects inside freertos task, when task is deleted with vTaskDelete the object is not destroyed (destructor is not called) which means there is no full cleanup;
i know its not usual use case and the only explanation is that task function never exit, because after vTaskDelete() there is "void" (i mean like cosmos void) just nothing more.

Here is test example:

Code: Select all

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"


class TestClass
{
private:
    uint8_t _val = 10;    
public:
    TestClass();
    ~TestClass();

    uint8_t val() {
        return _val;
    }
};

TestClass::TestClass()
{
    printf("constructor\n");
}

TestClass::~TestClass()
{
    _val = 0;
    printf("destructor\n");
}

static TestClass* obj_p = nullptr;

static void test_task(void* p)
{
//    { // uncomment brakets solve problem
    TestClass obj;
    obj_p = &obj;
    obj.val();
    vTaskDelay(200);
//    }
    printf("delete task\n");
    vTaskDelete(NULL);
}

extern "C" void app_main()
{
    xTaskCreate(test_task, "task", 4000, NULL, 1, NULL);
    vTaskDelay(100);
    while (obj_p->val())
    {
        printf("Val: %d\n", obj_p->val());
        vTaskDelay(50);
    }

    printf("end of main\n");
}
This is expected log output, which we can see after uncommenting brackets:

Code: Select all

constructor
Val: 10
Val: 10
destructor
delete task
end of main
Any thoughts?

boarchuz
Posts: 605
Joined: Tue Aug 21, 2018 5:28 am

Re: freertos task does not cleanup c++ class objects on vTaskDelete

Postby boarchuz » Tue Feb 13, 2024 3:50 am

vTaskDelete doesn't return so obj never goes out of scope so obj's destructor never runs. This is expected. C++ has no way of "knowing" what vTaskDelete(NULL) does, it can't be cleaning up anything upon arbitrary function calls that it expects to return from.

A separate function or your commented brackets seem like decent solutions.

chegewara
Posts: 2364
Joined: Wed Jun 14, 2017 9:00 pm

Re: freertos task does not cleanup c++ class objects on vTaskDelete

Postby chegewara » Tue Feb 13, 2024 4:15 am

Yeah, but it was the optimistic version, with brackets.

Now the worst possible case scenario:
- the same situation with task, locally created class objects
- task is deleted from another task with vTaskDelete()
- class object is never deleted, no matter if we use brackets or not

I am using esp32 for years and espressif is assuring us on github and forum, whenever someone is mentioning memory leaks when task is deleted, that freertos is releasing all resources, if not immediately then after some cycles. All objects on stack should be cleaned up, and class objects are put on the stack.
Looks like its not true.

This especially has impact on arduino users which are working mostly with C++ and classes and very often are using tasks.
This not only leads to memory leaks but also to unexplained memory corruptions and crashes.
Now myself i have to rethink how to use class objects with tasks.

MicroController
Posts: 1707
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: freertos task does not cleanup c++ class objects on vTaskDelete

Postby MicroController » Tue Feb 13, 2024 8:32 am

not sure what is the problem:
- freertos,
- esp-idf,
- compiler?
One way to look at it: The 'poblem' is that FreeRTOS is written in C and compiled as C code; it could not run any C++ destructors even if it wanted to.
chegewara wrote:
Tue Feb 13, 2024 4:15 am
Yeah, but it was the optimistic version, with brackets.

Now the worst possible case scenario:
- the same situation with task, locally created class objects
- task is deleted from another task with vTaskDelete()
- class object is never deleted, no matter if we use brackets or not
That's one of the reasons why you should never delete a task from ouside the task, neither in C nor in C++; i.o.w.: never pass anything other than NULL to vTaskDelete(...).
I am using esp32 for years and espressif is assuring us on github and forum, whenever someone is mentioning memory leaks when task is deleted, that freertos is releasing all resources, if not immediately then after some cycles. All objects on stack should be cleaned up, and class objects are put on the stack.
Seems to be a misunderstanding. vTaskDelete() does the opposite of xTaskCreate(): It releases the memory allocated for a task's stack and TCB. That's it. Yes, it releases the memory used by local variables (i.e. stack), but no other cleanup is done.
Not a problem for C structs, where you'd have to call any 'destructor' yourself anyway.
Now myself i have to rethink how to use class objects with tasks.
Maybe. The solutions are there:
1. Make sure your objects' lifetime/scope ends before vTaskDelete(), and
2. never vTaskDelete() any task except the current one, see above.

Who is online

Users browsing this forum: No registered users and 105 guests