Rendezvous (sync) pattern in FreeRTOS - Time & Space Complexity
When tasks wait for each other to reach a point before continuing, it affects how long the program runs.
We want to see how the waiting time grows as more tasks synchronize.
Analyze the time complexity of the following code snippet.
// Two tasks synchronize using a binary semaphore
SemaphoreHandle_t syncSemaphore;
void TaskA(void *params) {
// Do some work
xSemaphoreGive(syncSemaphore); // Signal TaskB
xSemaphoreTake(syncSemaphore, portMAX_DELAY); // Wait for TaskB
}
void TaskB(void *params) {
xSemaphoreTake(syncSemaphore, portMAX_DELAY); // Wait for TaskA
// Do some work
xSemaphoreGive(syncSemaphore); // Signal TaskA
}
This code shows two tasks using a semaphore to wait for each other before continuing.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Each task waits once on the semaphore before proceeding.
- How many times: The synchronization happens once per rendezvous point in each task.
As the number of tasks or rendezvous points increases, the waiting time grows linearly because each task must wait for others.
| Input Size (n) | Approx. Operations |
|---|---|
| 2 tasks | 2 waits and 2 signals |
| 10 tasks | 10 waits and 10 signals |
| 100 tasks | 100 waits and 100 signals |
Pattern observation: The number of synchronization operations grows directly with the number of tasks.
Time Complexity: O(n)
This means the waiting and signaling steps increase in a straight line as more tasks synchronize.
[X] Wrong: "Adding more tasks won't affect the waiting time much because they run in parallel."
[OK] Correct: Even if tasks run at the same time, each must wait for others at the rendezvous point, so waiting grows with more tasks.
Understanding how tasks wait for each other helps you design programs that run smoothly without unnecessary delays.
"What if we replaced the binary semaphore with a counting semaphore for multiple tasks? How would the time complexity change?"
Practice
Solution
Step 1: Understand the Rendezvous pattern concept
The Rendezvous pattern is designed to synchronize two tasks so that each waits for the other to reach a certain point before continuing.Step 2: Identify the purpose in FreeRTOS context
In FreeRTOS, this synchronization is often done using semaphores to signal readiness and wait for the other task.Final Answer:
To synchronize two tasks so they wait for each other before proceeding -> Option AQuick Check:
Rendezvous = Synchronize tasks [OK]
- Thinking it creates independent tasks without coordination
- Confusing with task prioritization
- Assuming it manages memory allocation
Solution
Step 1: Recall synchronization primitives in FreeRTOS
FreeRTOS provides mutexes, semaphores, queues, and event groups for task synchronization and communication.Step 2: Identify which primitive signals readiness and waits
The Rendezvous pattern requires signaling and waiting between two tasks, which is best done using semaphores.Final Answer:
Semaphore -> Option CQuick Check:
Rendezvous uses semaphores [OK]
- Choosing mutex which is for mutual exclusion, not sync
- Confusing queues with direct signaling
- Using event groups which are for multiple event flags
Solution
Step 1: Understand semaphore signaling and waiting
If Task A signals semaphore S1 before Task B waits on it, the semaphore count becomes available.Step 2: Analyze Task B's wait on S1
When Task B waits on S1, it finds the semaphore already signaled and unblocks immediately without waiting.Final Answer:
Task B will unblock immediately when it waits on S1 -> Option DQuick Check:
Signaled semaphore unblocks waiting task immediately [OK]
- Assuming tasks must wait even if semaphore is signaled
- Thinking Task A blocks waiting on S2 first
- Believing both tasks run without blocking always
void TaskA(void *pvParameters) {
xSemaphoreGive(SemA);
xSemaphoreTake(SemB, portMAX_DELAY);
// Continue...
}
void TaskB(void *pvParameters) {
xSemaphoreTake(SemA, portMAX_DELAY);
xSemaphoreGive(SemB);
// Continue...
}Solution
Step 1: Check semaphore initialization
Before using semaphores, they must be created with xSemaphoreCreateBinary or similar functions.Step 2: Identify missing creation
The code snippet does not show creation of SemA and SemB, which will cause runtime errors or undefined behavior.Final Answer:
Semaphores SemA and SemB are not created before use -> Option BQuick Check:
Semaphores must be created before giving or taking [OK]
- Thinking order of give/take is wrong here
- Believing semaphores are not for synchronization
- Misunderstanding portMAX_DELAY usage
Solution
Step 1: Understand semaphore types and their behavior
Binary semaphores can only hold one signal at a time, so signaling twice before waiting can cause lost signals and deadlock.Step 2: Identify solution to handle multiple signals
Counting semaphores can hold multiple signals, allowing tasks to signal multiple times without losing signals, preventing deadlock.Final Answer:
Use counting semaphores instead of binary semaphores -> Option AQuick Check:
Counting semaphores handle multiple signals safely [OK]
- Using mutexes which do not count signals
- Using queues which add complexity
- Using event groups with single bits only
Ready to prove it?
Pass the test and this pattern moves unknown
