Skip to content

fix: prevent engine_destroy crash on stale task handle#100

Merged
ladvoc merged 1 commit into
livekit:mainfrom
anujdeshpande:fix/engine-destroy-stale-task-handle
Jun 10, 2026
Merged

fix: prevent engine_destroy crash on stale task handle#100
ladvoc merged 1 commit into
livekit:mainfrom
anujdeshpande:fix/engine-destroy-stale-task-handle

Conversation

@anujdeshpande

@anujdeshpande anujdeshpande commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #85.

engine_destroy() could crash with a LoadProhibited exception (vTaskDeleteuxListRemove) because both the engine task and engine_destroy() raced to delete the same task:

  • engine_task() ends with vTaskDelete(NULL) (self-delete).
  • engine_destroy() waited a fixed vTaskDelay(100ms) and then called vTaskDelete(eng->task_handle).

If the task happened to exit during that 100 ms window — common after a server-side disconnect, where in-flight reconnect/timer/peer events wake the task blocked on xQueueReceive(portMAX_DELAY) — it self-deleted first, leaving eng->task_handle stale. engine_destroy() then called vTaskDelete() on the freed handle and crashed, which made it impossible to safely free room resources and leaked ~100 KB of internal RAM per session.

Fix

Replace the fixed delay + unconditional delete with an explicit join:

  • Add a binary "task done" semaphore, created before the task so the task can always signal completion.
  • The task gives the semaphore right before vTaskDelete(NULL).
  • engine_destroy() enqueues a new internal _EV_STOP event to wake the (possibly blocked) task so it observes is_running == false and exits promptly, then waits on the semaphore.
  • The handle is only force-deleted as a last resort if the task fails to exit within ENGINE_TASK_JOIN_TIMEOUT_MS (5 s).

This removes the double-delete entirely: in the normal path only the task deletes itself, and engine_destroy() just joins and clears the handle.

_EV_STOP is ignored by every state handler (default: break; → event freed), so waking the task has no side effects on the state machine.

Testing

  • Built the minimal example for esp32s3 with ESP-IDF v5.5.1 — compiles cleanly with no warnings on engine.c, image created successfully.
  • Verified control flow and ordering by inspection: the semaphore is created before the task; the task is joined before the queue/timer/signal/peers are torn down; _EV_STOP is a safe no-op for all states.

The change is contained to components/livekit/core/engine.c.

engine_destroy() could crash with a LoadProhibited exception when the
engine task self-deleted (vTaskDelete(NULL)) during the fixed 100ms
shutdown delay. Both the task and engine_destroy() raced to delete the
same task, so engine_destroy() could call vTaskDelete() on an
already-freed handle. This commonly happened after a server-side
disconnect, where in-flight reconnect events would wake the blocked task
during the destroy window.

Coordinate shutdown with a join semaphore instead: the task signals
completion before self-deleting, and engine_destroy() enqueues a stop
event to wake the (possibly blocked) task, then waits on the semaphore.
The handle is only force-deleted as a last resort if the task fails to
exit within a timeout.

Fixes livekit#85
@CLAassistant

CLAassistant commented Jun 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@ladvoc ladvoc self-requested a review June 10, 2026 01:21
@ladvoc

ladvoc commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

@anujdeshpande, thank you for your contribution—I will take a look.

@ladvoc ladvoc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM ✅, this will be included in the next release.

@ladvoc ladvoc merged commit 90936d9 into livekit:main Jun 10, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

engine_destroy crashes after server-side disconnect (vTaskDelete on stale handle)

3 participants