Fix unpublish_track deadlock on FFI error#752
Conversation
task_done() ran after the error raise, so a failed unpublish left the room queue's join() waiting forever and stalled the event loop.
There was a problem hiding this comment.
🔴 Track publishing errors stall the room event loop with the same deadlock fixed for unpublishing
The room queue event is not marked as done (queue.task_done() at livekit-rtc/livekit/rtc/participant.py:831) when a publish error is raised (livekit-rtc/livekit/rtc/participant.py:824), so the room event loop blocks forever waiting on the unconsumed event.
Impact: When publishing a track fails, the room stops processing all subsequent events, effectively freezing the connection.
Same deadlock pattern as the unpublish_track fix in this PR
This PR fixes the deadlock in unpublish_track by moving queue.task_done() before the error check. However, publish_track has the identical pattern:
queue = self._room_queue.subscribe()atlivekit-rtc/livekit/rtc/participant.py:816await queue.wait_for(...)atlivekit-rtc/livekit/rtc/participant.py:819— returns an event that the caller must mark done- Error check at
livekit-rtc/livekit/rtc/participant.py:823-824raisesPublishTrackErrorbeforetask_done()at line 831 - The
finallyblock at line 833-834 only callsunsubscribe, nottask_done()
The room event loop at livekit-rtc/livekit/rtc/room.py:714-715 does:
self._room_queue.put_nowait(event)
await self._room_queue.join()Since join() waits for all subscribers to call task_done(), the unconsumed event blocks the room event loop permanently.
The fix should mirror the unpublish_track fix: move queue.task_done() to immediately after wait_for returns, before the error check.
(Refers to lines 823-831)
Was this helpful? React with 👍 or 👎 to provide feedback.
unpublish_trackonly callsqueue.task_done()at the bottom of thetry, after the errorraise.So when the FFI reports an error the raise skips it, and that event stays on the room queue unconsumed.
Room._listen_taskdoesput_nowait(event)thenawait join(), so one stuck event hangs the whole room loop.Moved
task_done()to right afterwait_for()returns, before the error check. The error still propagates and the queue always gets released.Added a regression test that drives the same put/join loop. It hangs without the fix and passes with it.
on thing:
publish_trackright above has the same pattern. Left it out to keep this focused - happy to fix it here or in a follow-up.closes #565