Worked examples — FreeRTOS IPC — queues, semaphores, mutexes, event groups
5.5.11 · D3· Coding › Embedded Systems & Real-Time Software › FreeRTOS IPC — queues, semaphores, mutexes, event groups
Prerequisites jinpar hum rely karte hain: Producer–Consumer Pattern, RTOS Scheduling — preemption & priorities, Priority Inversion & the Mars Pathfinder bug, Interrupt Service Routines & Deferred Processing, Critical Sections & taskENTER_CRITICAL, Memory Management in FreeRTOS — heap_1..5.
The scenario matrix
Har FreeRTOS IPC bug ya exam question inhi cells mein se kisi ek mein aata hai. Ise quadrants ki tarah socho: do axes hain — "kaun sa object" aur "kaun si edge/degenerate condition."
| # | Object | Case class (the "sign / quadrant / degenerate") | Covered by |
|---|---|---|---|
| C1 | Queue | Normal FIFO, buffer mein room hai | Example 1 |
| C2 | Queue | Full queue → sender blocks / times out (edge) | Example 2 |
| C3 | Queue | Empty queue → receiver forever blocks (edge) | Example 1 |
| C4 | Queue | Zero-timeout poll (degenerate wait = 0) | Example 2 |
| C5 | Counting semaphore | Count walks , bursts (limiting value) | Example 3 |
| C6 | Counting semaphore | Over-give past max (saturation edge) | Example 3 |
| C7 | Binary semaphore | ISR → task wake-up, latency (real-world) | Example 4 |
| C8 | Binary sem | Give twice, Take once (lost-count degenerate) | Example 4 |
| C9 | Mutex | Priority inheritance timing (L, M, H ke "signs") | Example 5 |
| C10 | Mutex | Mutex ISR se use karna (illegal / degenerate) | Example 6 |
| C11 | Event group | Wait ALL (AND) vs ANY (OR), auto-clear | Example 7 |
| C12 | Event group | Ek set se kaafi tasks wake + stale-bit re-trigger (edge) | Example 7 |
| C13 | Pointer queue | Dangling-pointer ownership (exam twist) | Example 8 |
Har example neeche apni cells ko [C2, C4] ki tarah naam deta hai.
Example 1 — Queue: normal flow aur empty edge [C1, C3]
Forecast: receive order aur empty gap mein CPU ka behaviour guess karo, phir aage padho.
- Consumer ek empty queue hit karta hai.
Yeh step kyun? Pehle queue ki state jaanna zaroori hai. Zero items stored hain, isliye
xQueueReceivekuch copy nahi kar sakta. - Kernel consumer ko spin karne ki jagah block kar deta hai.
Yeh step kyun?
portMAX_DELAYmatlab "forever wait karo" (upar define hua). Kernel consumer ko waiting-to-receive list mein move karta hai aur ready list se hata deta hai — toh CPU ke ek bhi cycle ek tight loop mein empty queue check karne mein nahi jaate; woh baaki ready tasks run karta hai. Timeline figure dekho. - Producer
10send karta hai. Yeh step kyun? Ek queue meinSendjo ek blocked receiver ke paas ho, do kaam atomically karta hai:10ka byte pattern ek kernel slot mein copy karo, phir sabse high-priority wale waiting receiver ko unblock karo. - FIFO ordering.
Yeh step kyun? Queue FIFO hai (upar define hua). Kyunki sends
10, 20, 30order mein hote hain, buffer ka front hamesha sabse purana hoga. Toh receives10 → 20 → 30order mein aayenge.

10, 20, 30 time order mein hain; teal arrow pehla send receiver ko wake karta dikhata hai. Left-to-right padho: consumer sota hai, phir items oldest-first drain karta hai.
Verify: Buffer ne yahan ek baar mein 1 se zyada item kabhi nahi rakha (consumer turant drain karta hai), toh capacity 3 kabhi stressed nahi hui. Order out = order in = 10,20,30. Units: teen uint32_t copies, 4 bytes each. ✓
Example 2 — Queue: full buffer + zero-timeout poll [C2, C4]
Forecast: chaar sends mein se kitne success return karenge?
- Sends 1, 2, 3 slots 0, 1, 2 fill karte hain.
Yeh step kyun? Har
Sendnext free slot mein copy karta hai; 3 free slots exist hain. TeenopdTRUEreturn karte hain. - Send 4 queue ko full paata hai. Yeh step kyun? Capacity 3 hai, aur kissi ne receive nahi kiya, toh 0 slots free hain.
- Timeout 0 hai → no blocking, turant return.
Yeh step kyun? Zero
xTicksToWaitdegenerate wait hai: "try karo, par kabhi sleep mat karo." Abhi room nahi hai, toh callpdFALSEimmediately return karta hai.

1, 2, 3 label ke saath queue full capacity pe dikhata hai; dashed orange box 4 label ke saath fourth send reject hota dikhata hai, arrow bounce back karta hai aur caption "returns pdFALSE (no room, no wait)." Neeche ki line rule batati hai: succeeded = min(4, 3) = 3.
Verify: 3 successes + 1 failure = 4 attempts. Buffer exactly capacity = 3 items hold karta hai. Agar timeout portMAX_DELAY hota, toh send 4 block karta (fail nahi hota) jab tak consumer ek slot free na kare — isliye cell C2 aur cell C4 alag hain. ✓
Example 3 — Counting semaphore: burst arrivals + saturation [C5, C6]
Forecast: 5 gives ke baad count guess karo, aur task kitne takes kar sakta hai.
- Gives 1, 2, 3 count
0 → 1 → 2 → 3raise karte hain. Yeh step kyun? HarGivehai. Count literally hai "kaane wake-ups mujhe abhi bhi deni hain." - Gives 4 aur 5 ceiling hit karte hain.
Yeh step kyun? Count se zyada nahi ho sakta.
xSemaphoreGivepdFALSEreturn karta hai aur count saturate hota hai — woh do events silently lost ho jaate hain. - Task takes:
3 → 2 → 1 → 0, teen successes. Yeh step kyun? HarTakehai jab tak . Teen units available hain. - Chautha take paata hai → blocks. Yeh step kyun? ke saath Take blocking edge hai; task next give tak so jaata hai.

0→1→2→3 chadhta hai phir flat ho jaata hai (gives 4 aur 5, N=3 par plum dotted ceiling ke against lost hain); orange staircase phir teen takes ke liye 3→2→1→0 step karta hai, aur far right par label mark karta hai jahan count=0 take ko block karne par majboor karta hai.
Verify: gives requested 5, ceiling 3, toh 2 lost. Takes 3 drain karte hain, 0 bachi. Yahi exact reason hai ki tum semaphore ki max count ko worst-case burst ke hisaab se size karo — undersize karo toh events gayab ho jaate hain. ✓
Example 4 — Binary semaphore: ISR wake + the lost-count trap [C7, C8]
Forecast: do gives — task do bytes process karta hai ya ek?
- Pehla give: count
0 → 1. Yeh step kyun? Binary semaphore wala counting semaphore hai. Pehla give single flag set karta hai. - Doosra give: count
1pe rehta hai (saturated). Yeh step kyun? Max 1 hai, toh doosra give increment nahi kar sakta — yehpdFALSEreturn karta hai. "Do bytes aaye" ka fact ab invisible hai. - Task ek baar take karta hai: count
1 → 0, phir blocks. Yeh step kyun? Sirf ek unit exist karta hai, toh task exactly ek baar wake hota hai aur ek byte process karta hai. Doosre byte ka signal lost ho gaya. - Fix — kitne hain count karo, na ki hua ya nahi. Yeh step kyun? Bursts handle karne ke liye ya tum actual bytes ki queue drain karo, ya counting semaphore use karo jo FIFO depth ke hisaab se size ho (Example 3 dekho). Binary ka matlab "kya hua?", "kitni baar?" nahi.
Verify: gives = 2, lekin unit stored, toh wakes = 1, lost = 1. par counting formula ke saath consistent. ✓
Example 5 — Mutex: priority inheritance timeline [C9]
Forecast: wait ke dauran L ki effective priority guess karo, aur kaun run karta hai.
- L mutex hold karta hai, critical section run karta hai. Yeh step kyun? Baseline establish karo: low-priority task lock own karta hai.
- H take karne ki koshish karta hai, owned paata hai, blocks. Yeh step kyun? Mutex held hai, toh H (priority 3) proceed nahi kar sakta aur mutex ke wait list par park ho jaata hai.
- Inheritance ke bina: M (priority 2) L (priority 1) ko preempt karta hai. Yeh step kyun? Scheduler hamesha highest-priority ready task run karta hai. M > L, toh M run karta hai — possibly forever. H (priority 3) indirectly M se block hai = priority inversion. H ka wait = "M jitna run kare." Unbounded.
- Inheritance ke saath: kernel L ko H ki priority (3) tak boost karta hai. Yeh step kyun? Jab H kisi L ki owned mutex par wait kare, FreeRTOS L ki effective priority tak raise karta hai. Ab M (2) < L-boosted (3), toh M L ko preempt nahi kar sakta. L critical section jaldi finish karta hai, mutex give karta hai, aur priority 1 par wapas aa jaata hai. Phir H run karta hai.

Verify: Inheritance ke baad, running order hai L(boosted) → H → M, aur H ka wait L ke remaining critical-section time se bounded hai, M se nahi. Yahi precisely Priority Inversion & the Mars Pathfinder bug fix hai. Priorities restore hoti hain: L back to 1. ✓
Example 6 — Mutex from an ISR: the illegal case [C10]
Forecast: kya yeh compile-and-work karta hai, silently corrupt hota hai, ya by design fail hota hai?
- Mutex ka ek owner hota hai; ISR ki koi task identity nahi. Yeh step kyun? Ownership "kaun si task yeh hold karti hai" ke roop mein stored hoti hai. Ek ISR koi task nahi hai, toh owner ke roop mein record karne ke liye koi nahi.
- Priority inheritance ISR ke liye undefined hai.
Yeh step kyun? Inheritance owner ki task priority boost karta hai. Koi owner task nahi hai, toh boost karne ke liye kuch nahi — poora mechanism meaningless hai. FreeRTOS isliye mutexes ke liye koi
xSemaphoreTakeFromISRprovide nahi karta. - Sahi pattern: ek binary/counting semaphore se task ko signal karo.
Yeh step kyun? ISR
xSemaphoreGiveFromISR(binarySem, &woke)karta hai phirportYIELD_FROM_ISR(woke); ek task mutex leta hai aur bus ka kaam karta hai. Yeh Interrupt Service Routines & Deferred Processing respect karta hai.
Verify: ISR se mutex lene ka koi valid FreeRTOS API exist nahi karta — degenerate case API level par hi reject hota hai, sirf discourage nahi kiya jaata. Sahi path semaphore-give + yield use karta hai. ✓
Example 7 — Event group: AND vs OR, broadcast to many, stale-bit re-trigger [C11, C12]
Forecast: WIFI-only set par kaun wake hoga (ek waiter ya kai?) guess karo, aur kya B aur C stale bits par dobara fire karte hain.
- Masks numerically compute karo.
Yeh step kyun?
WIFI = 1,CFG = 2. Group value apne set bits ka OR hai, toh hum har waiter ki condition us value ke against test karte hain. - Worker
WIFIset karta hai → group =01= 1. Teeno waiters test karo. Yeh step kyun? Ek bit-set har blocked task ko ek saath broadcast hota hai — yeh event-group ka woh superpower hai jo ek akela semaphore nahi kar sakta.- Task B (OR on {WIFI,CFG}):
1 & 3 = 1 ≠ 0→ B wakes. - Task C (OR on {WIFI}):
1 & 1 = 1 ≠ 0→ C wakes. - Task A (AND on {WIFI,CFG}):
1 & 3 = 1 ≠ 3→ A blocked rehta hai. TohWIFIke ek set ne do tasks (B aur C) simultaneously wake kiye — cell C12 ka "one set wakes many."
- Task B (OR on {WIFI,CFG}):
- Worker
CFGset karta hai → group =11= 3. Task A ka AND ab satisfy hua. Yeh step kyun?3 & 3 = 3 = mask→ A wakes. A ke paasclearOnExit = pdTRUEtha, toh exit par bits 0 aur 1 clear karta hai → group wapas00ho jaata hai. - Stale-bit re-trigger — woh C12 edge jo B aur C hit karte hain.
Yeh step kyun? Maano A abhi tak run nahi hua, toh
WIFIabhi bhi set hai, aur B ya C dobara wait par loop karte hain. Kyunki B aur C neclearOnExit = pdFALSEset kiya, bit kabhi consume nahi hui. Unki OR conditiongroup & mask ≠ 0abhi bhi true hai, toh woh stale bit par turant dobara wake hote hain — ek spurious re-trigger. Fix: woh task jo har event par ek baar react karna chahta hai useclearOnExit = pdTRUEset karna chahiye (jaise A karta hai), taki bit consume ho aur aagla cycle fresh start kare.

00 (start) → 01 (set WIFI) → 11 (set CFG). group=01 par do plum/orange arrows dono OR waiters (B aur C) ko fan out karte hain, ek set se kaafi tasks wake dikhate hain. group=11 par ek akela arrow AND waiter A ko wake karta hai, jo phir group auto-clear karke 00 par le jaata hai. Dashed loop-back arrow mark karta hai jahan ek non-clearing waiter stale WIFI bit par dobara fire karta.
Verify: A/B ke liye mask = 3, C ke liye = 1. group=1 par: B 1&3=1≠0 wakes, C 1&1=1≠0 wakes, A 1&3=1≠3 waits → 2 wake, 1 blocks. group=3 par: A 3&3=3=mask wakes, phir 3 & ~3 = 0 clear karta hai. Stale re-trigger: bit abhi bhi set aur clearOnExit=pdFALSE ke saath, 1 & 1 = 1 ≠ 0 abhi bhi true hai → C re-fires. ✓
Example 8 — Pointer through a queue: the dangling-pointer twist [C13]
Forecast: send ne pdTRUE return kiya — kya 512-byte packet safe hai jab consumer finally ise read kare? Aage padhne se pehle guess karo.
- Queue pointer value copy karta hai, 512 bytes nahi.
Yeh step kyun? Queue ka item size yahan
sizeof(char*)hai — ek typical 32-bit MCU par yeh 4 bytes hai. Toh sirfbufka address queue slot mein copy hota hai; 512 payload bytes wahan hi rehte hain jahan heap par allocate hue the. Isliye "copying bachne ke liye pointer send karo" mechanically kaam karta hai. - Producer consumer ke padhne se pehle
buffree karta hai. Yeh step kyun?free(buf)woh 512-byte heap block allocator ko wapas karta hai. Queue mein baitha 4-byte pointer ab woh memory naam karta hai jo allocator free aur reusable samajhta hai — ek dangling pointer (deallocated memory ki taraf point karta hai).heap_2/4/5freed blocks reuse karte hain (heap_1kabhi free nahi karta) iske liye Memory Management in FreeRTOS — heap_1..5 dekho. - Consumer dereference karta hai → garbage ya corrupted data padhta hai.
Yeh step kyun?
freeaur consumer ke read ke beech, kahin aur koi allocation exactly woh block grab kar sakta hai aur overwrite kar sakta hai. Consumer phir kisi aur ka data padhta hai — ek use-after-free bug jo intermittent aur debug karne mein brutal hota hai. - Fix: ownership transfer karo — producer ko free nahi karna chahiye.
Yeh step kyun? Buffer ki ownership pointer ke saath queue se travel karti hai. Rule: producer allocate kare aur send kare, phir kabhi
bufko touch na kare; consumer use karne ke baadbuffree kare. Ek baar mein exactly ek owner, send par hand-off. Choti items ke liye, pura problem by value copy karke avoid karo (struct khud send karo, pointer nahi).

free() heap block ko grey kar deta hai aur pointer ab dangle karta hai (freed memory mein dashed red arrow). Right: corrected hand-off — pointer queue cross karta hai, producer chod deta hai, aur consumer use ke baad free karta hai (single owner).
Verify: Queue ne 4 bytes move kiye; 512-byte payload kabhi copy nahi hui, toh uski lifetime puri tarah programmer ki responsibility hai. Producer mein free karna = use-after-free; consumer mein free karna = sahi single-owner handoff. Choti items ke liye, copy-by-value poora problem avoid karta hai. ✓
Recall Self-test (guess karne ke baad reveal karo)
Full queue (cap 3), timeout 0 ke saath send karo, 4 sends — kitne succeed karte hain? ::: 3 succeed karte hain, 1 pdFALSE return karta hai. Counting semaphore max 3, 0 se 5 baar given — final count aur lost events? ::: count = 3, 2 events lost. Binary semaphore ek take se pehle do baar given — bytes processed? ::: 1 (doosra give saturate hota hai aur lost ho jaata hai). Mutex, L=1 use hold karta hai, H=3 wait karta hai — wait ke dauran L ki effective priority? ::: 3 (H ke saath boosted). Event group, mask=3, group=1, {WIFI,CFG} par ek OR waiter aur {WIFI} par ek — kaun wake hota hai? ::: dono OR waiters wake hote hain (broadcast); AND waiter blocked rehta hai. Pointer send karo phir producer mein free karo — bug kya hai? ::: dangling pointer / use-after-free; consumer ko free own karna chahiye.
Parent topic par wapas jao · related: Producer–Consumer Pattern, Critical Sections & taskENTER_CRITICAL.