Skip to content

Navigation Menu

Sign in
Sign up

refactor(signaling): explicit signal lifecycle state machine - #1402

Draft
lukasIO wants to merge 6 commits into
main from
lukas/signal-state-machine
Draft

refactor(signaling): explicit signal lifecycle state machine #1402
lukasIO wants to merge 6 commits into
main from
lukas/signal-state-machine

Conversation

@lukasIO

@lukasIO lukasIO commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

SignalInner tracked its lifecycle in two fields, a stream slot and a reconnecting AtomicBool, that had to be kept in step by hand across restart, set_reconnected, close and send. It now holds one SignalState that owns the transport: Connected, Reconnecting, Offline, Disconnecting, Closed.

Every change goes through SignalState::transition, one match that is the whole table and also says which transport each move releases. An input a state does not accept is logged and refused; a resume from a state that cannot accept it fails with SignalError::InvalidState. The resume gate stays a state until the engine confirms, a stale transport cannot report in because SignalClient::restart awaits the old task first, and ReconnectFailed always lands in Offline.

The held-signal queue moves to a sync parking_lot Mutex that is never held across an await. The old async queue lock was taken in both orders relative to the stream lock, which with tokio's fair RwLock could deadlock against a pending restart writer. A send that fails with any transport error is now held like a SendError was.

SignalInner tracked its lifecycle in two fields, a stream slot and a
`reconnecting` AtomicBool, that had to be kept in step by hand across
restart, set_reconnected, close and send. It now holds one SignalState
that owns the transport: Connected, Reconnecting, Offline,
Disconnecting, Closed.
Every change goes through SignalState::transition, one match that is
the whole table and also says which transport each move releases. An
input a state does not accept is logged and refused; a resume from a
state that cannot accept it fails with SignalError::InvalidState. The
resume gate stays a state until the engine confirms, a stale transport
cannot report in because SignalClient::restart awaits the old task
first, and ReconnectFailed always lands in Offline.
The held-signal queue moves to a sync parking_lot Mutex that is never
held across an await. The old async queue lock was taken in both orders
relative to the stream lock, which with tokio's fair RwLock could
deadlock against a pending restart writer. A send that fails with any
transport error is now held like a SendError was.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

github-actions Bot commented Sep 4, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Changeset ✓

This PR includes a changeset covering all affected packages:

Package Bump
livekit patch
livekit-api patch
livekit-ffi patch
livekit-signaling patch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@ladvoc ladvoc Awaiting requested review from ladvoc ladvoc will be requested when the pull request is marked ready for review ladvoc is a code owner

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /