xTaskNotifyGive() as lightweight semaphore in FreeRTOS - Time & Space Complexity
We want to understand how the time cost changes when using xTaskNotifyGive() as a lightweight semaphore in FreeRTOS.
Specifically, how does the number of operations grow when tasks notify each other?
Analyze the time complexity of the following code snippet.
void Task1(void *pvParameters) {
for (;;) {
// Do some work
xTaskNotifyGive(Task2Handle); // Notify Task2
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void Task2(void *pvParameters) {
for (;;) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Wait for notification
// Process after notification
}
}
This code shows one task notifying another using xTaskNotifyGive() as a lightweight semaphore to signal events.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The notification send and receive calls inside infinite loops.
- How many times: Each task loops forever, sending or waiting for notifications repeatedly.
Each notification operation happens once per loop cycle, independent of any input size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 notifications sent and received |
| 100 | 100 notifications sent and received |
| 1000 | 1000 notifications sent and received |
Pattern observation: The number of operations grows linearly with the number of notification events, but each event is a simple, constant-time operation.
Time Complexity: O(n)
This means the total time grows linearly with the number of notifications, but each notification send or receive takes a constant amount of time.
[X] Wrong: "Using xTaskNotifyGive() causes time to grow with the number of notifications queued."
[OK] Correct: Notifications do not queue up like messages; each call is a simple flag set or clear, so the time per call stays constant.
Understanding how lightweight synchronization like xTaskNotifyGive() works helps you write efficient multitasking code and shows you know how to reason about task communication costs.
"What if we replaced xTaskNotifyGive() with a queue send operation? How would the time complexity change?"
Practice
xTaskNotifyGive() in FreeRTOS?Solution
Step 1: Understand the function role
xTaskNotifyGive()is designed to send a notification signal from one task to another.Step 2: Compare with other options
Creating or deleting tasks and memory allocation are unrelated toxTaskNotifyGive().Final Answer:
To send a simple signal from one task to another as a lightweight semaphore -> Option AQuick Check:
Signal sending = To send a simple signal from one task to another as a lightweight semaphore [OK]
- Confusing it with task creation or deletion functions
- Thinking it manages memory
- Assuming it blocks tasks directly
xTaskNotifyGive() to a task handle named xTaskHandle?Solution
Step 1: Recall function signature
xTaskNotifyGive()takes a task handle as a parameter, not a pointer or extra arguments.Step 2: Check each option
xTaskNotifyGive(xTaskHandle); correctly passes the task handle. xTaskNotifyGive(&xTaskHandle); passes address (wrong), C adds an extra argument (wrong), D has no argument (wrong).Final Answer:
xTaskNotifyGive(xTaskHandle); -> Option CQuick Check:
Correct syntax = xTaskNotifyGive(xTaskHandle); [OK]
- Passing pointer to task handle instead of handle itself
- Adding extra parameters
- Calling without any argument
void Task1(void *pvParameters) {
// Wait for notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// Continue after notification
printf("Task1 resumed\n");
}
void Task2(void *pvParameters) {
// Notify Task1
xTaskNotifyGive(Task1Handle);
}
What will be printed when Task2 calls xTaskNotifyGive()?Solution
Step 1: Understand notification flow
Task1waits for a notification withulTaskNotifyTake(). WhenTask2callsxTaskNotifyGive(), it sends the notification.Step 2: Effect of notification
The notification unblocksTask1, so it prints "Task1 resumed".Final Answer:
Task1 resumed -> Option BQuick Check:
Notification received = Task1 prints [OK]
- Assuming Task1 never resumes
- Confusing which task prints output
- Thinking code causes compile error
xTaskNotifyGive():
void TaskA(void *pvParameters) {
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
printf("TaskA running\n");
}
void TaskB(void *pvParameters) {
xTaskNotifyGive(&TaskAHandle);
}Solution
Step 1: Check parameter passed to xTaskNotifyGive()
The function expects a task handle, but&TaskAHandlepasses its address, which is incorrect.Step 2: Validate other parts
UsingpdFALSEis valid to clear notification count, no syntax error in printf, and delay is optional.Final Answer:
Passing address of task handle instead of task handle to xTaskNotifyGive() -> Option AQuick Check:
Correct parameter type needed = Passing address of task handle instead of task handle to xTaskNotifyGive() [OK]
- Passing pointer to handle instead of handle
- Misunderstanding pdFALSE effect
- Assuming printf syntax error
xTaskNotifyGive() as a lightweight semaphore to synchronize two tasks: Producer and Consumer. The Consumer waits for the notification before processing data. Which sequence correctly implements this synchronization?Solution
Step 1: Identify roles of tasks
The Consumer should wait for a signal before processing, so it usesulTaskNotifyTake()to block until notified.Step 2: Producer signals Consumer
The Producer sends the notification usingxTaskNotifyGive()with the Consumer's task handle.Final Answer:
Consumer calls ulTaskNotifyTake(pdTRUE, portMAX_DELAY); to wait, Producer calls xTaskNotifyGive(ConsumerHandle); to signal -> Option DQuick Check:
Wait then signal = Consumer callsulTaskNotifyTake(pdTRUE, portMAX_DELAY);to wait, Producer callsxTaskNotifyGive(ConsumerHandle);to signal [OK]
- Reversing roles of Producer and Consumer
- Both tasks signaling without waiting
- Using wrong task handles in calls
Ready to prove it?
Pass the test and this pattern moves unknown
