Quality RTOS & Embedded Software

KERNEL

RTOS Task Notifications
[Inter-task communication and synchronisation]



Description

[Also see the blog post “Decrease RAM Footprint and Accelerate Execution with FreeRTOS Notifications”]

Each RTOS task has an array of task notifications. Each task notification has a notification state that can be either ‘pending’ or ‘not pending’, and a 32-bit notification value. The constant configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the task notification array. Prior to FreeRTOS V10.4.0 tasks only had a single task notification, not an array of notifications.

A direct to task notification is an event sent directly to a task, rather than indirectly to a task via an intermediary object such as a queue, event group or semaphore. Sending a direct to task notification to a task sets the state of the target task notification to ‘pending’. Just as a task can block on an intermediary object such as a semaphore to wait for that semaphore to be available, a task can block on a task notification to wait for that notification’s state to become pending.

Sending a direct to task notification to a task can also optionally update the value of the target notification in one of the following ways:

  • Overwrite the value regardless of whether the receiving task has read the value being overwritten or not.
  • Overwrite the value, but only if the receiving task has read the value being overwritten.
  • Set one or more bits in the value.
  • Increment (add one to) the value.
Calling xTaskNotifyWait()/xTaskNotifyWaitIndexed() to read a notification value clears that notification’s state to ‘not pending’. Notification states can also be explicitly set to ‘not pending’ by calling xTaskNotifyStateClear()/xTaskNotifyStateClearIndexed().

Note: Each notification within the array operates independently – a task can only block on one notification within the array at a time and will not be unblocked by a notification sent to any other array index.

RTOS task notification functionality is enabled by default, and can be excluded from a build (saving 8 bytes per array index per task) by setting configUSE_TASK_NOTIFICATIONS to 0 in FreeRTOSConfig.h.

IMPORTANT NOTE: FreeRTOS Stream and Message Buffers use the task notification at array index 0. If you want to maintain the state of a task notification across a call to a Stream or Message Buffer API function then use a task notification at an array index greater than 0.


Performance Benefits and Usage Restrictions

The flexibility of task notifications allows them to be used where otherwise it would have been necessary to create a separate queue, binary semaphore, counting semaphore or event group. Unblocking an RTOS task with a direct notification is 45% faster * and uses less RAM than unblocking a task using an intermediary object such as a binary semaphore. As would be expected, these performance benefits require some use case limitations:
  1. RTOS task notifications can only be used when there is only one task that can be the recipient of the event. This condition is however met in the majority of real world use cases, such as an interrupt unblocking a task that will process the data received by the interrupt.

  2. Only in the case where an RTOS task notification is used in place of a queue: While a receiving task can wait for a notification in the Blocked state (so not consuming any CPU time), a sending task cannot wait in the Blocked state for a send to complete if the send cannot complete immediately.


Use Cases

Notifications are sent using the xTaskNotifyIndexed() and xTaskNotifyGiveIndexed() API functions (and their interrupt safe equivalents), and remain pending until the receiving RTOS task calls either of the xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() API functions. Each of these API functions has an equivalent that does not have the “Indexed” prefix. The non “Indexed” versions always operate on the task notification at array index 0. For example xTaskNotifyGive( TargetTask ) is equivalent to xTaskNotifyGiveIndexed( TargetTask, 0 ) – both increment the task notification at index 0 of the task referenced by the task handled TargetTask.

Examples


* Measured using the binary semaphore implementation from FreeRTOS V8.1.2, compiled with GCC at -O2 optimisation, and without configASSERT() defined. A 35% improvement can still be obtained using the improved binary semaphore implementation found in FreeRTOS V8.2.0 and higher.





Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.