Binary semaphore creation and usage in FreeRTOS - Time & Space Complexity
We want to understand how the time cost changes when using binary semaphores in FreeRTOS.
Specifically, how the operations scale when tasks take or give the semaphore.
Analyze the time complexity of the following code snippet.
SemaphoreHandle_t xSemaphore;
void setup() {
xSemaphore = xSemaphoreCreateBinary();
}
void task1() {
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
// critical section
xSemaphoreGive(xSemaphore);
}
}
This code creates a binary semaphore and a task that waits to take it, enters a critical section, then gives it back.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The task repeatedly tries to take and give the semaphore.
- How many times: This happens each time the task runs and needs the resource.
Each semaphore take or give is a single operation regardless of how many tasks exist.
| Input Size (number of semaphore operations) | Approx. Operations |
|---|---|
| 10 | 10 |
| 100 | 100 |
| 1000 | 1000 |
Pattern observation: The time grows linearly with the number of semaphore operations.
Time Complexity: O(n)
This means the time spent grows directly with how many times the semaphore is taken and given.
[X] Wrong: "The time complexity of using semaphores is O(1) regardless of the number of operations."
[OK] Correct: Each take or give takes constant time, but for n operations the total time is O(n).
Understanding how synchronization tools like semaphores scale helps you write efficient multitasking programs and shows you know how to reason about system performance.
"What if we changed the binary semaphore to a counting semaphore? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of binary semaphores
Binary semaphores are used to signal availability or control access to resources between tasks.Step 2: Compare options with this role
Only To signal and control access to shared resources between tasks describes signaling and controlling access, which matches the purpose of binary semaphores.Final Answer:
To signal and control access to shared resources between tasks -> Option BQuick Check:
Binary semaphore = resource signaling [OK]
- Confusing binary semaphore with counting semaphore
- Thinking it stores multiple values
- Assuming it creates tasks
Solution
Step 1: Recall the function to create a binary semaphore
The correct function is xSemaphoreCreateBinary() with no parameters.Step 2: Check each option
xSemaphoreCreateBinary(); matches the correct function. xSemaphoreCreateCounting(1, 0); creates a counting semaphore, C creates a mutex, and D is invalid syntax.Final Answer:
xSemaphoreCreateBinary(); -> Option AQuick Check:
Create binary semaphore = xSemaphoreCreateBinary() [OK]
- Using counting semaphore creation instead
- Adding parameters to xSemaphoreCreateBinary()
- Confusing mutex creation with binary semaphore
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();
void Task1(void *pvParameters) {
if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
// Critical section
}
}
void Task2(void *pvParameters) {
xSemaphoreGive(xBinarySemaphore);
}
What will happen when Task1 runs before Task2?Solution
Step 1: Analyze xSemaphoreTake with portMAX_DELAY
Task1 calls xSemaphoreTake with portMAX_DELAY, so it waits indefinitely for the semaphore.Step 2: Consider initial semaphore state and Task2 action
The semaphore is created but not given initially, so Task1 blocks until Task2 calls xSemaphoreGive.Final Answer:
Task1 will block indefinitely until Task2 gives the semaphore -> Option DQuick Check:
xSemaphoreTake with portMAX_DELAY blocks until given [OK]
- Assuming semaphore is initially available
- Thinking Task1 runs critical section immediately
- Confusing blocking with error
SemaphoreHandle_t xBinarySemaphore = xSemaphoreCreateBinary();
void Task(void *pvParameters) {
if (xSemaphoreTake(xBinarySemaphore, 0) == pdTRUE) {
// Do work
xSemaphoreGive(xBinarySemaphore);
}
}Solution
Step 1: Check initial semaphore state after creation
xSemaphoreCreateBinary creates a semaphore that is initially empty (not given).Step 2: Analyze xSemaphoreTake with zero timeout
With zero timeout, xSemaphoreTake returns immediately. Since semaphore is empty, it returns pdFALSE, so the if block never runs.Final Answer:
Semaphore is never given before take, so take always fails -> Option CQuick Check:
Binary semaphore empty at creation, take with zero timeout fails [OK]
- Assuming semaphore is initially given
- Thinking zero timeout means wait forever
- Confusing function parameters
Solution
Step 1: Understand synchronization with binary semaphore
Task B must wait for Task A's signal, so Task B should take the semaphore and block until given.Step 2: Match sequence to this logic
Task A gives the semaphore after finishing work; Task B takes it with portMAX_DELAY to wait indefinitely.Final Answer:
Task A calls xSemaphoreGive() after work; Task B calls xSemaphoreTake() with portMAX_DELAY before work -> Option AQuick Check:
Give after work, take before work = sync tasks [OK]
- Reversing give and take roles
- Both tasks giving without taking
- Taking without giving semaphore
Ready to prove it?
Pass the test and this pattern moves unknown
