HomeAbout UsContact Us

Fixing FreeRTOS Event Group Timer Queue Overflow in ISR Context

By Jithin Tom
Published in Embedded OS
September 07, 2026
3 min read
Fixing FreeRTOS Event Group Timer Queue Overflow in ISR Context

Table Of Contents

01
Problem Statement: Lost Events in High-Frequency ISRs
02
Root Cause Analysis: Deferred Interrupt Processing
03
Detection Techniques: Proper Error Checking
04
Solution 1: Tuning FreeRTOS Configuration
05
Solution 2: Transitioning to Task Notifications (Recommended)
06
Summary
07
Related Reading
08
References
09
Frequently Asked Questions

FreeRTOS event groups are a powerful synchronization primitive for coordinating tasks, allowing a task to block on multiple events simultaneously. However, using them to signal events from a high-frequency Interrupt Service Routine (ISR) often leads to seemingly inexplicable lost events. This article explores the root cause of this issue—Timer Command Queue Overflow—and provides robust, high-performance solutions for deterministic ISR-to-task communication.

Problem Statement: Lost Events in High-Frequency ISRs

Consider a scenario where an ISR is triggered by a high-frequency source (e.g., a 100 kHz ADC conversion or a fast communication peripheral) and attempts to signal a processing task using xEventGroupSetBitsFromISR().

During low-load conditions, the system appears to work perfectly. However, under heavy CPU load or during bursts of high-frequency interrupts, the receiving task misses events. The system silently drops synchronization signals, leading to data loss or stalled state machines.

Root Cause Analysis: Deferred Interrupt Processing

To understand why events are lost, we must look at how xEventGroupSetBitsFromISR() is implemented within the FreeRTOS kernel.

Unlike direct-to-task notifications or queues, setting an event group bit is a non-deterministic operation. A single bit change could unblock multiple tasks, requiring the RTOS to traverse the event group’s waiting list, move tasks to the Ready list, and evaluate whether a context switch is required. Performing this potentially lengthy, unbounded operation directly within an ISR would violate deterministic interrupt latency constraints.

To solve this, FreeRTOS uses deferred interrupt processing. When you call xEventGroupSetBitsFromISR(), it does not set the bit. Instead, it sends a command message to the RTOS Daemon Task (also known as the Timer Service Task). The daemon task then executes the actual bit-setting operation in a task context.

The problem arises in the communication channel between the ISR and the daemon task: the Timer Command Queue.

  1. The length of this queue is defined by configTIMER_QUEUE_LENGTH in FreeRTOSConfig.h.
  2. Under the hood, xEventGroupSetBitsFromISR() invokes xTimerPendFunctionCallFromISR(). This requires both configUSE_TIMERS and INCLUDE_xTimerPendFunctionCall to be defined as 1.
  3. If the ISR fires and calls xEventGroupSetBitsFromISR() faster than the daemon task can read from the queue (e.g., if a higher-priority task preempts prvTimerTask, or the queue is sized too small), the queue saturates.
  4. Once the queue is full, subsequent calls to xEventGroupSetBitsFromISR() return pdFAIL (evaluating to pdFALSE), and the event bit command is silently discarded.
+-----------------------------------------------------------------------------+
| Deferred ISR Processing (Event Groups) |
+-----------------------------------------------------------------------------+
| |
| +--------------+ xEventGroupSetBitsFromISR() +----------------------+ |
| | Hardware ISR | ------------------------------> | Timer Command Queue | |
| +--------------+ | (configTIMER_QUEUE_ | |
| | LENGTH) | |
| +----------+-----------+ |
| | |
| v |
| +--------------+ Unblocks Task +----------+-----------+ |
| | Waiting Task | <------------------------------ | RTOS Daemon Task | |
| +--------------+ | (prvTimerTask) | |
| +----------------------+ |
| |
+-----------------------------------------------------------------------------+
| Direct-to-Task Notification Alternative |
+-----------------------------------------------------------------------------+
| |
| +--------------+ xTaskNotifyFromISR(..., eSetBits, ...) |
| | Hardware ISR | ---------------------------------------------+ |
| +--------------+ | |
| v |
| +----------------------+ |
| | Target Task TCB | |
| | - Zero queue alloc | |
| | - Direct bit update | |
| | - O(1) determinism | |
| +----------------------+ |
| |
+-----------------------------------------------------------------------------+

Detection Techniques: Proper Error Checking

The most common mistake when using event groups in ISRs is ignoring the return value. To detect timer command queue overflows, you must verify that the command was successfully queued.

1. Check the Return Value

Always verify the result of xEventGroupSetBitsFromISR() and clear your hardware interrupt flags deterministically:

void vTimer1ISR(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xResult;
/* Clear hardware interrupt flag first */
CLEAR_TIMER1_INTERRUPT_FLAG();
/* Attempt to set bit 0 via deferred daemon queue */
xResult = xEventGroupSetBitsFromISR(xTimerEventGroup, 0x01, &xHigherPriorityTaskWoken);
if (xResult != pdPASS) {
/* CRITICAL FAILURE: Timer command queue is full. Event is lost! */
vRecordDroppedEvent();
}
/* Request context switch if the daemon task or waiting task unblocked */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

2. Monitor Queue High-Water Mark

During development, use FreeRTOS trace macros or kernel-aware debugging tools to monitor the maximum usage of the timer command queue. If it frequently nears configTIMER_QUEUE_LENGTH, you are at risk of dropping events.

Solution 1: Tuning FreeRTOS Configuration

If you must use event groups (for instance, if a task needs to block on multiple bits originating from different ISRs), you can tune the RTOS configuration to mitigate the issue:

  1. Increase Queue Length: Increase configTIMER_QUEUE_LENGTH in FreeRTOSConfig.h to handle larger bursts of interrupts.
    #define configTIMER_QUEUE_LENGTH 50 // Increase based on available RAM and burst size
  2. Elevate Daemon Task Priority: Ensure the timer daemon task has a high enough priority to preempt other application tasks and process the queue quickly.
    #define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) // Highest priority

Note: While tuning helps with bursty traffic, it will not solve sustained over-saturation where the ISR rate exceeds the daemon task’s processing rate.

For high-frequency signaling from an ISR to a single task, Direct-to-Task Notifications are the superior alternative.

Task notifications are significantly faster, use less RAM, and most importantly, they do not use the timer command queue. The operation xTaskNotifyFromISR() sets the bits directly in the target task’s TCB (Task Control Block) from within the ISR, making it highly deterministic and immune to daemon task queue overflows.

Refactoring to Task Notifications

Here is how you replace an event group with a task notification using the eSetBits action:

Task Implementation:

/* The task handle must be known to the ISR */
extern TaskHandle_t xProcessingTaskHandle;
void vProcessingTask(void *pvParameters)
{
uint32_t ulNotifiedValue;
for(;;) {
/* Wait indefinitely for bit 0 or bit 1 to be set */
xTaskNotifyWait(0x00, /* Don't clear any bits on entry */
0xFFFFFFFF, /* Clear all bits on exit */
&ulNotifiedValue, /* Receives the notification value */
portMAX_DELAY); /* Block indefinitely */
if ((ulNotifiedValue & 0x01) != 0) {
/* Process Event 0 */
}
if ((ulNotifiedValue & 0x02) != 0) {
/* Process Event 1 */
}
}
}

ISR Implementation:

void vHighFrequencyISR(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* Directly set bit 0 in the task's notification value */
xTaskNotifyFromISR(xProcessingTaskHandle,
0x01,
eSetBits,
&xHigherPriorityTaskWoken);
/* Clear hardware interrupt flag */
CLEAR_PERIPHERAL_INT_FLAG();
/* Yield if the receiving task has a higher priority than the currently running task */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

Summary

When an ISR signals a task at high frequencies using FreeRTOS Event Groups, dropped events are rarely caused by a bitwise overflow. Instead, they are caused by Timer Command Queue Overflow due to the deferred nature of xEventGroupSetBitsFromISR(). By checking return values, properly tuning the RTOS daemon task, and preferentially using Direct-to-Task Notifications for high-speed signaling, you can guarantee deterministic and reliable ISR-to-task communication.

References

  1. FreeRTOS Kernel Reference Manual, V10.5.1, https://github.com/FreeRTOS/FreeRTOS-Kernel/releases/tag/V10.5.1
  2. Richard Barry, “Mastering the FreeRTOS Real Time Kernel”, 2021.

Frequently Asked Questions

Why are events lost when using FreeRTOS Event Groups from an ISR?

Calling `xEventGroupSetBitsFromISR()` does not set the bits directly. Instead, it defers the operation by sending a message to the FreeRTOS timer daemon task. If the ISR executes faster than the timer task can process these commands, the timer command queue overflows, causing `xEventGroupSetBitsFromISR()` to return `pdFAIL` and drop the event.

How can I detect timer command queue overflow?

Check the return value of `xEventGroupSetBitsFromISR()`. If it returns `pdFAIL` (or `pdFALSE`), the timer command queue is full. Additionally, monitor the high-water mark of the timer command queue during development to ensure it is sized correctly.

What is the recommended alternative for high-frequency ISR to task signaling?

For high-frequency signaling, prefer Direct-to-Task Notifications (`xTaskNotifyFromISR` with `eSetBits` or `vTaskNotifyGiveFromISR`). These are significantly faster, deterministic, and do not rely on the timer daemon task's queue.

Tags

freertosevent-groupqueue-overflowisrtask-notifications

Share


Previous Article
Accelerating Embedded AI Inference with CMSIS-NN on Cortex-M4
Jithin Tom

Jithin Tom

A Closer Look at C/C++, RTOS, and Embedded Systems

Related Posts

Fixing FreeRTOS Software Timer Callback Overruns
Fixing FreeRTOS Software Timer Callback Overruns
August 26, 2026
7 min
© 2026, All Rights Reserved.
Powered By Netlyft

Quick Links

Advertise with usAbout UsContact Us

Social Media