Calling vTaskDelay in loop() leads to infamous vListInsert death spiral
cversek opened this issue · 3 comments
This problem took me a long time to figure out which line of code was responsible for my sketch hanging at a distant call.
Symptoms:
- calling xQueueReceive on an empty queue with a finite timeout would cause a hanging of the kernel
- debugging pointed to a forever loop in the function vListInsert (which is surprisingly common and can be caused by lots of things!)
Eventually I tracked it down to a line in the loop()
function calling vTaskDelay
Now, since I thought loop
is iterated by the Idle Task, shouldn't this be allowed?
I'm using an Adafruit STM32F405 Feather board, with setting:
#define configMEMMANG_HEAP_NB 3
Anyway, here is a simple sketch to reproduce the issue:
#include <STM32FreeRTOS.h>
QueueHandle_t mQueue = NULL;
// HeartBeatTask, pulse the LED every couple of seconds
static void vHeartBeatTask(void* arg) {
UNUSED(arg);
pinMode(LED_BUILTIN, OUTPUT);
while (1) {
// Sleep for 2000 milliseconds.
vTaskDelay((2000L * configTICK_RATE_HZ) / 1000L);
// Turn LED on.
digitalWrite(LED_BUILTIN, HIGH);
// Sleep for 200 milliseconds.
vTaskDelay((200L * configTICK_RATE_HZ) / 1000L);
// Turn LED off.
digitalWrite(LED_BUILTIN, LOW);
}
}
static void vQueueRecvTask(void* arg) {
UNUSED(arg);
uint8_t pkt;
// Sleep for 5000 milliseconds.
vTaskDelay(pdMS_TO_TICKS(5000));
while (1) {
// attempt to receive from queue
Serial.println(F("# trying xQueueReceive"));
if (xQueueReceive(mQueue,&pkt,pdMS_TO_TICKS(5000)) == pdTRUE){
Serial.print(F("# recvd pkt: "));
Serial.println(pkt);
} else{ //xQueueReceive timed-out
Serial.println(F("# xQueueReceive empty"));
}
}
}
void setup() {
Serial.begin(115200);
while (!Serial){}; //WAIT FOR SERIAL CONNECTION TO OPEN, DEBUG ONLY!
// queues
mQueue = xQueueCreate(1,1);
xTaskCreate(vHeartBeatTask,NULL,configMINIMAL_STACK_SIZE+50,NULL,1,NULL);
xTaskCreate(vQueueRecvTask,NULL,configMINIMAL_STACK_SIZE+50,NULL,1,NULL);
// start FreeRTOS
vTaskStartScheduler();
// should never return
Serial.println(F("Die"));
assert_param(false);
}
void loop() {
// WARNING this seems to cause sketch to hang after xQueueReceive blocks
vTaskDelay(pdMS_TO_TICKS(10));
}
Why called it on loop?
Because I don't know any better. Let's say I wanted to blink an LED in the Idle task? Is blocking the Idle task bad form? Should that lead to possible corruption of the scheduler data?
Well all is explained here:
STM32FreeRTOS/src/STM32FreeRTOS.c
Lines 136 to 144 in 727e46a