5.5.11 · D3 · HinglishEmbedded Systems & Real-Time Software

Worked examplesFreeRTOS IPC — queues, semaphores, mutexes, event groups

4,017 words18 min read↑ Read in English

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.

  1. Consumer ek empty queue hit karta hai. Yeh step kyun? Pehle queue ki state jaanna zaroori hai. Zero items stored hain, isliye xQueueReceive kuch copy nahi kar sakta.
  2. Kernel consumer ko spin karne ki jagah block kar deta hai. Yeh step kyun? portMAX_DELAY matlab "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.
  3. Producer 10 send karta hai. Yeh step kyun? Ek queue mein Send jo ek blocked receiver ke paas ho, do kaam atomically karta hai: 10 ka byte pattern ek kernel slot mein copy karo, phir sabse high-priority wale waiting receiver ko unblock karo.
  4. FIFO ordering. Yeh step kyun? Queue FIFO hai (upar define hua). Kyunki sends 10, 20, 30 order mein hote hain, buffer ka front hamesha sabse purana hoga. Toh receives 10 → 20 → 30 order mein aayenge.

Figure — FreeRTOS IPC — queues, semaphores, mutexes, event groups
Figure — upar ki band (plum) consumer ko BLOCKED state mein CPU free ke saath park dikhata hai; teen orange boxes producer ke sends 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?

  1. Sends 1, 2, 3 slots 0, 1, 2 fill karte hain. Yeh step kyun? Har Send next free slot mein copy karta hai; 3 free slots exist hain. Teeno pdTRUE return karte hain.
  2. Send 4 queue ko full paata hai. Yeh step kyun? Capacity 3 hai, aur kissi ne receive nahi kiya, toh 0 slots free hain.
  3. Timeout 0 hai → no blocking, turant return. Yeh step kyun? Zero xTicksToWait degenerate wait hai: "try karo, par kabhi sleep mat karo." Abhi room nahi hai, toh call pdFALSE immediately return karta hai.

Figure — FreeRTOS IPC — queues, semaphores, mutexes, event groups
Figure — teen filled teal slots 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.

  1. Gives 1, 2, 3 count 0 → 1 → 2 → 3 raise karte hain. Yeh step kyun? Har Give hai. Count literally hai "kaane wake-ups mujhe abhi bhi deni hain."
  2. Gives 4 aur 5 ceiling hit karte hain. Yeh step kyun? Count se zyada nahi ho sakta. xSemaphoreGive pdFALSE return karta hai aur count saturate hota hai — woh do events silently lost ho jaate hain.
  3. Task takes: 3 → 2 → 1 → 0, teen successes. Yeh step kyun? Har Take hai jab tak . Teen units available hain.
  4. Chautha take paata hai → blocks. Yeh step kyun? ke saath Take blocking edge hai; task next give tak so jaata hai.

Figure — FreeRTOS IPC — queues, semaphores, mutexes, event groups
Figure — teal staircase pehle teen gives ke liye 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?

  1. Pehla give: count 0 → 1. Yeh step kyun? Binary semaphore wala counting semaphore hai. Pehla give single flag set karta hai.
  2. Doosra give: count 1 pe rehta hai (saturated). Yeh step kyun? Max 1 hai, toh doosra give increment nahi kar sakta — yeh pdFALSE return karta hai. "Do bytes aaye" ka fact ab invisible hai.
  3. 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.
  4. 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.

  1. L mutex hold karta hai, critical section run karta hai. Yeh step kyun? Baseline establish karo: low-priority task lock own karta hai.
  2. 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.
  3. 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.
  4. 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.

Figure — FreeRTOS IPC — queues, semaphores, mutexes, event groups
Figure — teen horizontal lanes H(3), M(2), L(1) label ke saath. L ki lane mein teal bar L ko mutex hold karte huye priority 3 tak boosted dikhata hai; M ki lane mein dashed orange box M ko "blocked out (2<3)" dikhata hai — woh boosted L ko preempt nahi kar sakta. Plum band H ka wait short aur bounded dikhata hai, "H runs" ke saath end hota hai jab L mutex give kare.

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?

  1. 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.
  2. 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 xSemaphoreTakeFromISR provide nahi karta.
  3. Sahi pattern: ek binary/counting semaphore se task ko signal karo. Yeh step kyun? ISR xSemaphoreGiveFromISR(binarySem, &woke) karta hai phir portYIELD_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.

  1. 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.
  2. Worker WIFI set 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 ≠ 0B wakes.
    • Task C (OR on {WIFI}): 1 & 1 = 1 ≠ 0C wakes.
    • Task A (AND on {WIFI,CFG}): 1 & 3 = 1 ≠ 3A blocked rehta hai. Toh WIFI ke ek set ne do tasks (B aur C) simultaneously wake kiye — cell C12 ka "one set wakes many."
  3. Worker CFG set karta hai → group = 11 = 3. Task A ka AND ab satisfy hua. Yeh step kyun? 3 & 3 = 3 = maskA wakes. A ke paas clearOnExit = pdTRUE tha, toh exit par bits 0 aur 1 clear karta hai → group wapas 00 ho jaata hai.
  4. 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 WIFI abhi bhi set hai, aur B ya C dobara wait par loop karte hain. Kyunki B aur C ne clearOnExit = pdFALSE set kiya, bit kabhi consume nahi hui. Unki OR condition group & mask ≠ 0 abhi 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 use clearOnExit = pdTRUE set karna chahiye (jaise A karta hai), taki bit consume ho aur aagla cycle fresh start kare.

Figure — FreeRTOS IPC — queues, semaphores, mutexes, event groups
Figure — upar ke teal boxes group value time ke saath hain: 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.

  1. 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 sirf buf ka 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.
  2. Producer consumer ke padhne se pehle buf free 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/5 freed blocks reuse karte hain (heap_1 kabhi free nahi karta) iske liye Memory Management in FreeRTOS — heap_1..5 dekho.
  3. Consumer dereference karta hai → garbage ya corrupted data padhta hai. Yeh step kyun? free aur 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.
  4. 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 buf ko touch na kare; consumer use karne ke baad buf free 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).

Figure — FreeRTOS IPC — queues, semaphores, mutexes, event groups
Figure — left: ek 512-byte teal heap block jisme ek 4-byte orange pointer queue slot ke andar use point karta hai (sirf pointer copy hua). Middle: producer ka 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.