Select your cookie preferences

We use essential cookies and similar tools that are necessary to provide our site and services. We use performance cookies to collect anonymous statistics, so we can understand how customers use our site and make improvements. Essential cookies cannot be deactivated, but you can choose “Customize” or “Decline” to decline performance cookies.

If you agree, AWS and approved third parties will also use cookies to provide useful site features, remember your preferences, and display relevant content, including relevant advertising. To accept or decline all non-essential cookies, choose “Accept” or “Decline.” To make more detailed choices, choose “Customize.”

Updated Jul 2025

RTOS Task Notifications

Used As Light Weight Binary Semaphores

Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM than unblocking a task with a binary semaphore. This page demonstrates how this is done.

A binary semaphore is a semaphore that has a maximum count of 1, hence the 'binary' name. A task can only 'take' the semaphore if it is available, and the semaphore is only available if its count is 1.

When a task notification is used in place of a binary semaphore the receiving task's notification value is used in place of the binary semaphore's count value, and the ulTaskNotifyTake() (or ulTaskNotifyTakeIndexed()) API function is used in place of the semaphore's xSemaphoreTake() API function. The ulTaskNotifyTake() function's xClearOnExit parameter is set to pdTRUE so the count value is returned to zero each time the notification is taken - emulating a binary semaphore.

Likewise the xTaskNotifyGive() (or xTaskNotifyGiveIndexed()) or vTaskNotifyGiveFromISR() (or vTaskNotifyGiveIndexedFromISR()) functions are used in place of the semaphore's xSemaphoreGive() and xSemaphoreGiveFromISR() functions.

See the example below.

1/* This is an example of a transmit function in a generic
2 peripheral driver. An RTOS task calls the transmit function,
3 then waits in the Blocked state (so not using an CPU time)
4 until it is notified that the transmission is complete. The
5 transmission is performed by a DMA, and the DMA end interrupt
6 is used to notify the task. */
7
8/* Stores the handle of the task that will be notified when the
9 transmission is complete. */
10static TaskHandle_t xTaskToNotify = NULL;
11
12/* The index within the target task's array of task notifications
13 to use. */
14const UBaseType_t xArrayIndex = 1;
15
16/* The peripheral driver's transmit function. */
17void StartTransmission( uint8_t *pcData, size_t xDataLength )
18{
19 /* At this point xTaskToNotify should be NULL as no transmission
20 is in progress. A mutex can be used to guard access to the
21 peripheral if necessary. */
22 configASSERT( xTaskToNotify == NULL );
23
24 /* Store the handle of the calling task. */
25 xTaskToNotify = xTaskGetCurrentTaskHandle();
26
27 /* Start the transmission - an interrupt is generated when the
28 transmission is complete. */
29 vStartTransmit( pcData, xDatalength );
30}
31/*-----------------------------------------------------------*/
32
33/* The transmit end interrupt. */
34void vTransmitEndISR( void )
35{
36BaseType_t xHigherPriorityTaskWoken = pdFALSE;
37
38 /* At this point xTaskToNotify should not be NULL as
39 a transmission was in progress. */
40 configASSERT( xTaskToNotify != NULL );
41
42 /* Notify the task that the transmission is complete. */
43 vTaskNotifyGiveIndexedFromISR( xTaskToNotify,
44 xArrayIndex,
45 &xHigherPriorityTaskWoken );
46
47 /* There are no transmissions in progress, so no tasks
48 to notify. */
49 xTaskToNotify = NULL;
50
51 /* If xHigherPriorityTaskWoken is now set to pdTRUE then a
52 context switch should be performed to ensure the interrupt
53 returns directly to the highest priority task. The macro used
54 for this purpose is dependent on the port in use and may be
55 called portEND_SWITCHING_ISR(). */
56 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
57}
58/*-----------------------------------------------------------*/
59
60/* The task that initiates the transmission, then enters the
61 Blocked state (so not consuming any CPU time) to wait for it
62 to complete. */
63void vAFunctionCalledFromATask( uint8_t ucDataToTransmit,
64 size_t xDataLength )
65{
66uint32_t ulNotificationValue;
67const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 );
68
69 /* Start the transmission by calling the function shown above. */
70 StartTransmission( ucDataToTransmit, xDataLength );
71
72 /* Wait to be notified that the transmission is complete. Note
73 the first parameter is pdTRUE, which has the effect of clearing
74 the task's notification value back to 0, making the notification
75 value act like a binary (rather than a counting) semaphore. */
76 ulNotificationValue = ulTaskNotifyTakeIndexed( xArrayIndex,
77 pdTRUE,
78 xMaxBlockTime );
79
80 if( ulNotificationValue == 1 )
81 {
82 /* The transmission ended as expected. */
83 }
84 else
85 {
86 /* The call to ulTaskNotifyTake() timed out. */
87 }
88}