Limits of vTaskDelay timeout and cancellation behavior in FreeRTOS
0 reputation · 06 Nov 2024, 13:06 UTC
0 reputation · 06 Nov 2024, 13:06 UTC
FreeRTOS v10.x exposes vTaskDelay() as a documented tick‑based timeout primitive. The API suspends the calling task for a fixed number of tick units, but it does not provide a direct cancel‑stop operation. When a higher‑priority task unblocks the delayed task via a notification, the delay is aborted immediately; however, the underlying tick count continues to advance, potentially leading to unexpected re‑entries if the task is resumed before the original timeout expires.
The kernel’s handling of a delayed task that is deleted or suspended is not explicitly documented. Current behavior simply leaves the task in the delayed state, which may cause missed wake‑ups or duplicate activations when the task is later resumed or recreated. Additionally, the effect of changing configTICK_RATE_HZ at runtime on already pending delays is unclear.
vTaskDelay()?configTICK_RATE_HZ affect the timing of a task that is already in a delayed state?35275 reputation · 06 Nov 2024, 18:54 UTC
Short answer: vTaskDelay() has no cancellation API, but the kernel does clean up after it. Deleting a delayed task removes its delay-list entry and the pending timeout with it. Suspending a delayed task does not freeze its timeout — the ticks keep counting. And changing configTICK_RATE_HZ at runtime is not a supported operation, so pending delays under a changed tick rate are simply undefined behavior, not a documented edge case.
When vTaskDelete() runs against a task sitting in the delayed list, the kernel removes that task's list item from the delayed (or overflow-delayed) list as part of TCB teardown. There is no lingering timeout: the TCB is freed (by the idle task in the default configuration), the handle becomes invalid, and nothing will \"wake up\" later. If you recreate a task with the same priority/stack, it starts with a fresh TCB and no memory of the old delay. So the answer to \"does FreeRTOS clear the remaining delay\" is yes — implicitly, because the delay state lives in the TCB's list item, which is destroyed with the task.
vTaskSuspend() moves the task out of the delayed list into the suspended list, but the tick value stored in the task's list item is the absolute wake tick, not a countdown. When you later call vTaskResume(), the kernel re-evaluates that stored wake time against the current tick count. Two outcomes:
So the timeout does not resume \"from where it left off\" in the sense of pausing a stopwatch; it resumes against an absolute deadline that kept running. This is the likely explanation for the \"duplicate activations\" symptom in the question: a task resumed after its deadline fires immediately, which can look like a spurious extra wake-up if the code assumed the full delay would re-run.
A task blocked in vTaskDelay() alone cannot be unblocked early by a notification — vTaskDelay() doesn't wait on a notification object. Early-unblock behavior only applies to the interruptible waits (xTaskNotifyWait(), ulTaskNotifyTake(), queue/semaphore receives with a timeout). If you're seeing a delay \"aborted\" by a notification, the task is almost certainly blocked on one of those primitives with a tick timeout, not in a bare vTaskDelay(). That distinction matters for diagnosis.
configTICK_RATE_HZ is a compile-time constant used to derive the tick compare value programmed into the timer. FreeRTOS provides no API to change it at runtime. If you reprogram the timer frequency yourself mid-run, every absolute wake tick already stored in delayed list items is now measured against a different tick period — delays become proportionally too long or too short, and nothing in the kernel compensates. Treat this as unsupported; if you need a different tick rate, change it at build time and reboot.
// Deletion: start a task delaying 5000 ticks, delete it after 100.\n// Expected: no further activity from that handle; idle task frees the TCB.\n\n// Suspension: task delays 1000 ticks; suspend at tick 200, resume at tick 1200.\n// Expected: task becomes ready immediately on resume (deadline already passed),\n// not 800 ticks after resume.Instrument with xTaskGetTickCount() at block and wake points and compare against the expected absolute deadline.
This describes the default tick-driven implementation in v10.x. Tickless-idle ports and v11.x rework some delay-list handling, so verify against your exact kernel version and port. If your symptom involves early wake-ups, the one detail worth confirming is which blocking primitive the task actually uses — that changes the diagnosis entirely.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.