Skip to content

WebRTCSession startup failures leak event-loop resources #2807

Description

@voropaevv

Summary

When a lazily started WebRTCSession fails during its first connection attempt, the session remains in the STARTED state and the owned asyncio event loop and daemon thread remain alive. A failure after RTCPeerConnection construction can also leave the partial peer connection unreachable by cleanup.

This makes the failed session appear usable while retaining resources until process shutdown.

Reproduction

I reproduced both failure boundaries deterministically on current main (a6caf5e75f76a31c6cc41eb167c01ef0a2e23f76) with stubbed I/O:

Failure before peer creation

  1. Construct a WebRTCSession.
  2. Make _init() raise before creating a peer connection.
  3. Call _ensure_started().
  4. Inspect the lifecycle state and owned resources.

Observed after the exception:

  • session._state == SessionState.STARTED
  • the event loop is still running and open
  • the daemon loop thread is still alive
  • source cleanup was not called

Failure after peer creation

  1. Let _init() create an RTCPeerConnection.
  2. Raise while configuring the source.
  3. Call _ensure_started().
  4. Inspect the partial peer.

Observed after the exception:

  • session._pc is None, because the peer is assigned only at the end of _init()
  • peer.close() was not called
  • the source and loop-thread resources remain active

The test harness explicitly stops the leaked parent loop after recording these observations.

Root cause

  • _ensure_started() changes the state to STARTED before initialization but has no failure transition or teardown path.
  • _init_connection() starts an owned event-loop thread but does not stop, join, or close it when initialization fails.
  • _init() publishes self._pc only after all later setup and signaling steps succeed.
  • An unfinished startup future is not cancelled if an external BaseException interrupts future.result().

Expected behavior

After any startup failure:

  • the session enters the terminal CLOSED state
  • the original exception and HTTP diagnostics remain intact
  • an unfinished startup future is cancelled before teardown
  • a partially created peer connection and source are each cleaned up once
  • pending loop tasks are cancelled and drained
  • the event loop is stopped, its thread is joined, and the loop is closed by its owner thread
  • a later _ensure_started() call fails with Cannot use closed WebRTCSession

The fix should reuse the existing single-flight close() path and should not add session restart semantics or change callback, queue, or server-side behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions