Free heap memory after deleting task

Hello,

I am using FreeRTOS port for the Teensy 4.0

I have a program where I have a task that listens for serial input and when there is an input. This task will create another task that will execute some code and delete itself. it looks something like this in code:

static void TriggerTaskCallBack(void *arg)
{
while (true)
{
if (Serial.available() > 0)
{
portBASE_TYPE sortingTask = xTaskCreate(SortingTaskCallback, “Sorting Task”, configMINIMAL_STACK_SIZE, NULL, 2, NULL);
}
}
}

static void SortingTaskCallback(void *arg)
{
//processing happens here (mainly I2C writes)
vTaskDelete(NULL);
}

setup
{
portBASE_TYPE TriggerTask = xTaskCreate(TriggerTaskCallBack, “Trigger Task”, configMINIMAL_STACK_SIZE, NULL, 1, NULL);
}

loop{}

vTaskDelete() does not free the heap space used by the “SortingTask” task that’s being created within TriggerTaskCallback. So after many calls to xTaskCreate(SortingTaskCallback,) there is no more heap memory and I cannot create more tasks.

Please keep in mind that I am very new to FreeRTOS and my code is implemented this way because I need to process each Serial input as they come, so the best solution I though of is to create a “temporary” task each time and process that Serial input. if another Serial input is received while still processing the prior input. the Trigger Task will create a new Sorting task to process that new Serial input. if there is a better way to implement this logic, please let me know.

Thank you,

First to answer the question:

Memory is automatically allocated from the heap when you create a task using xTaskCreate() (rather than xTaskCreateStatic()). The allocated memory is for the the task’s stack, and its internal data structure used by the kernel (the task control block, or TCB). If one task deletes another task then that memory is automatically freed by the task that calls vTaskDelete(). If a task deletes itself (by calling vTaskDelete( NULL )) then the memory is freed by the idle task - so if the idle task never runs the memory won’t get freed. Your TriggerTaskCallBack() task has a priority of 1, which is above the priority of the idle task, and never blocks, so it starves the idle task of memory - hence you will eventually run out of heap. It is better to structure TriggerTaskCallBack() to be event driven - by which I mean make it block to wait for a serial character rather than poll for a serial character. The idle task will then run while it is the blocked state, cleaning up the memory. I would recommend reading the first few chapters of this book: Free RTOS Book and Reference Manual

Any memory allocated by the task itself by calling pvPortMalloc() won’t get freed automatically.

Next a comment about the code structure:

Generally it is a not a good idea to keep creating and deleting tasks because it is not very efficient (and can lead to other problems over time). Consider having two tasks that get created at initialisation time then communicate with each other using a queue or stream buffer - so SortingTaskCallback() blocks to wait for a message from TriggerTaskCallBack() before performing its operation, rather than performing its operation as soon as it is created. Again the book already linked above will help.

1 Like

Thank you very much for your reply.

I would like to expand on your code structure suggestion if you don’t mind.

My goal is to run the SortingTaskCallback() each time a serial input is received by TriggerTaskCallBack(). I need to run SortingTaskCallback() as soon as the Serial input is received.
An example:
Lets assume SortingTaskCallback() takes 3 seconds to complete its processing

=> at t = 0s, a Serial input is “captured” by TriggerTaskCallBack()
=>SortingTaskCallback() begins running for 3 seconds
=> meanwhile at t=1s, another Serial input is “captured” by SortingTaskCallback()
=>another “instance” of SortingTaskCallback() should begin running for 3 seconds
=> at t=3s, first SortingTaskCallback() is done processing;
=> at t=4s, second SortingTaskCallback() is also done processing.

How can I implement this? it works by creating each time a task as I showed in my post and I understand the problems it might cause, how can a queue help me achieve a similar result?

First comment, you are ‘mixing’ two different ‘operating systems’ that aren’t very compatible. FreeRTOS and the ‘Teensy Library’. The ‘Serial.available()’ interface isn’t good for a ‘Real Time’ application, as it is based on polling.

I once worked on an application based on a similar machine, and while we keep parts of the library in place to support loading and debugging, for things like serial we just rewrote things to use an interrupt-based I/O approach using blocking primitives. (In our case for the serial port, Queue). A task wanting to read from the serial port would block on a queue.

Second, unless you really need for two of your 'SortingTask’s to be able to run at the same time (if that even makes sense), then I wouldn’t create and then destroy the task, but make it a long-running task that waits to be signaled and then does its work and then block. The key thing to think about is what issue ar going to happen with whatever gets the results if the results of two sorting requests get intermixed.

If the issue is that ‘sorting’ has a part that is CPU intensive, then an I/O intensive part that you want to be able to overlap with the next sorting, then make sorting two tasks, the first that does the CPU intensive, then the second be I/O intensive.

1 Like