Note: this is a horrible design pattern, and ephemeral FreeRTOS tasks should not be created. See my FreeRTOS best practices bullet #17 here:
[SAFETY-CRITICAL] Use software timers and callbacks, not short-lived (temporary) tasks that just time out, do something, and delete themselves.
…but I’m stuck in a legacy code base and fixing things one-at-a-time, so for now…
What’s the best way (perhaps even better: what are various valid ways) to see if a FreeRTOS task has been deleted? Currently, I’m tracking its handle and setting it to NULL
before calling vTaskDelete()
on it, like this. Notice I’m calling myTaskHandle = NULL;
before vTaskDelete(NULL);
:
static TaskHandle_t myTaskHandle = NULL;
void myTask(void* params)
{
for (;;)
{
// do stuff
// - the code may exit this for loop to be killed under
// certain conditions
}
// Manually force the handle to NULL to track when I've deleted it!
myTaskHandle = NULL;
vTaskDelete(NULL); // Delete self
}
// This function can be called multiple times, so it must ensure it doesn't
// try to create the same task again until it's been previously deleted.
void myTaskInit()
{
// Here I check if the task is already running, and don't spawn it again
// if it is.
if (myTaskHandle != NULL)
{
printf("Error: myTask task already running");
return;
}
static StackType_t taskStack[configMINIMAL_STACK_SIZE];
static StaticTask_t taskControlBlock;
myTaskHandle = xTaskCreateStatic(
myTask,
"My Task",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY + 1,
taskStack,
&taskControlBlock);
}
Is there a better way to see if the task is deleted or not? Currently I’m doing the if (myTaskHandle != NULL)
check.
There’s a little bit of a race condition here, too:
myTaskHandle = NULL;
vTaskDelete(NULL);
It’s possible that myTask
gets preempted by another task right after myTask
calls myTaskHandle = NULL;
but before it calls vTaskDelete(NULL)
. If this happens, and the other task calls myTaskInit()
before myTask
gets a chance to call vTaskDelete(NULL)
, then myTaskInit()
will think that myTask
is already deleted (since its handle is NULL
) and recreate the task on top of an already-existing task which is about to be deleted! This will result in undefined behavior.
So, part of me thinks I need these guards:
taskENTER_CRITICAL(); // disable FreeRTOS context switches
myTaskHandle = NULL;
vTaskDelete(NULL);
taskEXIT_CRITICAL(); // re-enable FreeRTOS context switches
…but I don’t think that will work, because the taskEXIT_CRITICAL();
line will never be reached after vTaskDelete(NULL);
is called, right? Or am I wrong?
Note also that I don’t think eTaskGetState()
can tell me if the task is deleted. One of the valid states is indeed Deleted
, but I think that means it is simply waiting for the idle task to clean it up, and once it does, it will no longer have a state at all.
State Returned Value ... Deleted eDeleted (the tasks TCB is waiting to be cleaned up)