Skip to content

Commit

Permalink
Fixed deadlock in peerconnection.go
Browse files Browse the repository at this point in the history
In some rare cases during ice connection stage
change may result in deadlock. This fix makes
iceConnectionState and connectionState atomic
which should prevent deadlock.
  • Loading branch information
digitalix authored and Sean-Der committed Jun 20, 2021
1 parent 11f6a65 commit 7948437
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 39 deletions.
52 changes: 17 additions & 35 deletions peerconnection.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ type PeerConnection struct {
currentRemoteDescription *SessionDescription
pendingRemoteDescription *SessionDescription
signalingState SignalingState
iceConnectionState ICEConnectionState
connectionState PeerConnectionState
iceConnectionState atomic.Value // ICEConnectionState
connectionState atomic.Value // PeerConnectionState

idpLoginURL *string

Expand All @@ -66,8 +66,8 @@ type PeerConnection struct {
rtpTransceivers []*RTPTransceiver

onSignalingStateChangeHandler func(SignalingState)
onICEConnectionStateChangeHandler func(ICEConnectionState)
onConnectionStateChangeHandler func(PeerConnectionState)
onICEConnectionStateChangeHandler atomic.Value // func(ICEConnectionState)
onConnectionStateChangeHandler atomic.Value // func(PeerConnectionState)
onTrackHandler func(*TrackRemote, *RTPReceiver)
onDataChannelHandler func(*DataChannel)
onNegotiationNeededHandler atomic.Value // func()
Expand Down Expand Up @@ -128,12 +128,12 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
lastAnswer: "",
greaterMid: -1,
signalingState: SignalingStateStable,
iceConnectionState: ICEConnectionStateNew,
connectionState: PeerConnectionStateNew,

api: api,
log: api.settingEngine.LoggerFactory.NewLogger("pc"),
}
pc.iceConnectionState.Store(ICEConnectionStateNew)
pc.connectionState.Store(PeerConnectionStateNew)

if !api.settingEngine.disableMediaEngineCopy {
pc.api = &API{
Expand Down Expand Up @@ -458,29 +458,21 @@ func (pc *PeerConnection) onTrack(t *TrackRemote, r *RTPReceiver) {
// OnICEConnectionStateChange sets an event handler which is called
// when an ICE connection state is changed.
func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) {
pc.mu.Lock()
defer pc.mu.Unlock()
pc.onICEConnectionStateChangeHandler = f
pc.onICEConnectionStateChangeHandler.Store(f)
}

func (pc *PeerConnection) onICEConnectionStateChange(cs ICEConnectionState) {
pc.mu.Lock()
pc.iceConnectionState = cs
handler := pc.onICEConnectionStateChangeHandler
pc.mu.Unlock()

pc.iceConnectionState.Store(cs)
pc.log.Infof("ICE connection state changed: %s", cs)
if handler != nil {
go handler(cs)
if handler := pc.onICEConnectionStateChangeHandler.Load(); handler != nil {
handler.(func(ICEConnectionState))(cs)
}
}

// OnConnectionStateChange sets an event handler which is called
// when the PeerConnectionState has changed
func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) {
pc.mu.Lock()
defer pc.mu.Unlock()
pc.onConnectionStateChangeHandler = f
pc.onConnectionStateChangeHandler.Store(f)
}

// SetConfiguration updates the configuration of this PeerConnection object.
Expand Down Expand Up @@ -714,9 +706,6 @@ func (pc *PeerConnection) createICEGatherer() (*ICEGatherer, error) {
// Update the PeerConnectionState given the state of relevant transports
// https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum
func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnectionState, dtlsTransportState DTLSTransportState) {
pc.mu.Lock()
defer pc.mu.Unlock()

connectionState := PeerConnectionStateNew
switch {
// The RTCPeerConnection object's [[IsClosed]] slot is true.
Expand All @@ -743,15 +732,14 @@ func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnection
connectionState = PeerConnectionStateConnecting
}

if pc.connectionState == connectionState {
if pc.connectionState.Load() == connectionState {
return
}

pc.log.Infof("peer connection state changed: %s", connectionState)
pc.connectionState = connectionState
handler := pc.onConnectionStateChangeHandler
if handler != nil {
go handler(connectionState)
pc.connectionState.Store(connectionState)
if handler := pc.onConnectionStateChangeHandler.Load(); handler != nil {
go handler.(func(PeerConnectionState))(connectionState)
}
}

Expand Down Expand Up @@ -1534,10 +1522,7 @@ func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) error {
// ICEConnectionState returns the ICE connection state of the
// PeerConnection instance.
func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
pc.mu.RLock()
defer pc.mu.RUnlock()

return pc.iceConnectionState
return pc.iceConnectionState.Load().(ICEConnectionState)
}

// GetSenders returns the RTPSender that are currently attached to this PeerConnection
Expand Down Expand Up @@ -1959,10 +1944,7 @@ func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
// ConnectionState attribute returns the connection state of the
// PeerConnection instance.
func (pc *PeerConnection) ConnectionState() PeerConnectionState {
pc.mu.Lock()
defer pc.mu.Unlock()

return pc.connectionState
return pc.connectionState.Load().(PeerConnectionState)
}

// GetStats return data providing statistics about the overall connection
Expand Down
8 changes: 4 additions & 4 deletions peerconnection_go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,17 +384,17 @@ func TestPeerConnection_PropertyGetters(t *testing.T) {
currentRemoteDescription: &SessionDescription{},
pendingRemoteDescription: &SessionDescription{},
signalingState: SignalingStateHaveLocalOffer,
iceConnectionState: ICEConnectionStateChecking,
connectionState: PeerConnectionStateConnecting,
}
pc.iceConnectionState.Store(ICEConnectionStateChecking)
pc.connectionState.Store(PeerConnectionStateConnecting)

assert.Equal(t, pc.currentLocalDescription, pc.CurrentLocalDescription(), "should match")
assert.Equal(t, pc.pendingLocalDescription, pc.PendingLocalDescription(), "should match")
assert.Equal(t, pc.currentRemoteDescription, pc.CurrentRemoteDescription(), "should match")
assert.Equal(t, pc.pendingRemoteDescription, pc.PendingRemoteDescription(), "should match")
assert.Equal(t, pc.signalingState, pc.SignalingState(), "should match")
assert.Equal(t, pc.iceConnectionState, pc.ICEConnectionState(), "should match")
assert.Equal(t, pc.connectionState, pc.ConnectionState(), "should match")
assert.Equal(t, pc.iceConnectionState.Load(), pc.ICEConnectionState(), "should match")
assert.Equal(t, pc.connectionState.Load(), pc.ConnectionState(), "should match")
}

func TestPeerConnection_AnswerWithoutOffer(t *testing.T) {
Expand Down

0 comments on commit 7948437

Please sign in to comment.