@@ -56,8 +56,9 @@ type PeerConnection struct {
56
56
idpLoginURL * string
57
57
58
58
isClosed * atomicBool
59
- isGracefulClosed * atomicBool
60
- isGracefulClosedDone chan struct {}
59
+ isGracefullyClosingOrClosed bool
60
+ isCloseDone chan struct {}
61
+ isGracefulCloseDone chan struct {}
61
62
isNegotiationNeeded * atomicBool
62
63
updateNegotiationNeededFlagOnEmptyChain * atomicBool
63
64
@@ -130,8 +131,8 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
130
131
ICECandidatePoolSize : 0 ,
131
132
},
132
133
isClosed : & atomicBool {},
133
- isGracefulClosed : & atomicBool {} ,
134
- isGracefulClosedDone : make (chan struct {}),
134
+ isCloseDone : make ( chan struct {}) ,
135
+ isGracefulCloseDone : make (chan struct {}),
135
136
isNegotiationNeeded : & atomicBool {},
136
137
updateNegotiationNeededFlagOnEmptyChain : & atomicBool {},
137
138
lastOffer : "" ,
@@ -2101,22 +2102,44 @@ func (pc *PeerConnection) GracefulClose() error {
2101
2102
func (pc * PeerConnection ) close (shouldGracefullyClose bool ) error {
2102
2103
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #1)
2103
2104
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2)
2104
- alreadyGracefullyClosed := shouldGracefullyClose && pc .isGracefulClosed .swap (true )
2105
- if pc .isClosed .swap (true ) {
2106
- if alreadyGracefullyClosed {
2107
- // similar but distinct condition where we may be waiting for some
2108
- // other graceful close to finish. Incorrectly using isClosed may
2109
- // leak a goroutine.
2110
- <- pc .isGracefulClosedDone
2111
- }
2112
- return nil
2105
+
2106
+ pc .mu .Lock ()
2107
+ // A lock in this critical section is needed because pc.isClosed and
2108
+ // pc.isGracefullyClosingOrClosed are related to each other in that we
2109
+ // want to make graceful and normal closure one time operations in order
2110
+ // to avoid any double closure errors from cropping up. However, there are
2111
+ // some overlapping close cases when both normal and graceful close are used
2112
+ // that should be idempotent, but be cautioned when writing new close behavior
2113
+ // to preserve this property.
2114
+ isAlreadyClosingOrClosed := pc .isClosed .swap (true )
2115
+ isAlreadyGracefullyClosingOrClosed := pc .isGracefullyClosingOrClosed
2116
+ if shouldGracefullyClose && ! isAlreadyGracefullyClosingOrClosed {
2117
+ pc .isGracefullyClosingOrClosed = true
2113
2118
}
2114
- if shouldGracefullyClose && ! alreadyGracefullyClosed {
2115
- defer close (pc .isGracefulClosedDone )
2119
+ pc .mu .Unlock ()
2120
+
2121
+ if isAlreadyClosingOrClosed {
2122
+ if ! shouldGracefullyClose {
2123
+ return nil
2124
+ }
2125
+ // Even if we're already closing, it may not be graceful:
2126
+ // If we are not the ones doing the closing, we just wait for the graceful close
2127
+ // to happen and then return.
2128
+ if isAlreadyGracefullyClosingOrClosed {
2129
+ <- pc .isGracefulCloseDone
2130
+ return nil
2131
+ }
2132
+ // Otherwise we need to go through the graceful closure flow once the
2133
+ // normal closure is done since there are extra steps to take with a
2134
+ // graceful close.
2135
+ <- pc .isCloseDone
2136
+ } else {
2137
+ defer close (pc .isCloseDone )
2116
2138
}
2117
2139
2118
- // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
2119
- pc .signalingState .Set (SignalingStateClosed )
2140
+ if shouldGracefullyClose {
2141
+ defer close (pc .isGracefulCloseDone )
2142
+ }
2120
2143
2121
2144
// Try closing everything and collect the errors
2122
2145
// Shutdown strategy:
@@ -2126,6 +2149,34 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error {
2126
2149
// continue the chain the Mux has to be closed.
2127
2150
closeErrs := make ([]error , 4 )
2128
2151
2152
+ doGracefulCloseOps := func () []error {
2153
+ if ! shouldGracefullyClose {
2154
+ return nil
2155
+ }
2156
+
2157
+ // these are all non-canon steps
2158
+ var gracefulCloseErrors []error
2159
+ if pc .iceTransport != nil {
2160
+ gracefulCloseErrors = append (gracefulCloseErrors , pc .iceTransport .GracefulStop ())
2161
+ }
2162
+
2163
+ pc .ops .GracefulClose ()
2164
+
2165
+ pc .sctpTransport .lock .Lock ()
2166
+ for _ , d := range pc .sctpTransport .dataChannels {
2167
+ gracefulCloseErrors = append (gracefulCloseErrors , d .GracefulClose ())
2168
+ }
2169
+ pc .sctpTransport .lock .Unlock ()
2170
+ return gracefulCloseErrors
2171
+ }
2172
+
2173
+ if isAlreadyClosingOrClosed {
2174
+ return util .FlattenErrs (doGracefulCloseOps ())
2175
+ }
2176
+
2177
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
2178
+ pc .signalingState .Set (SignalingStateClosed )
2179
+
2129
2180
closeErrs = append (closeErrs , pc .api .interceptor .Close ())
2130
2181
2131
2182
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #4)
@@ -2156,28 +2207,15 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error {
2156
2207
closeErrs = append (closeErrs , pc .dtlsTransport .Stop ())
2157
2208
2158
2209
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #8, #9, #10)
2159
- if pc .iceTransport != nil {
2160
- if shouldGracefullyClose {
2161
- // note that it isn't canon to stop gracefully
2162
- closeErrs = append (closeErrs , pc .iceTransport .GracefulStop ())
2163
- } else {
2164
- closeErrs = append (closeErrs , pc .iceTransport .Stop ())
2165
- }
2210
+ if pc .iceTransport != nil && ! shouldGracefullyClose {
2211
+ // we will stop gracefully in doGracefulCloseOps
2212
+ closeErrs = append (closeErrs , pc .iceTransport .Stop ())
2166
2213
}
2167
2214
2168
2215
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11)
2169
2216
pc .updateConnectionState (pc .ICEConnectionState (), pc .dtlsTransport .State ())
2170
2217
2171
- if shouldGracefullyClose {
2172
- pc .ops .GracefulClose ()
2173
-
2174
- // note that it isn't canon to stop gracefully
2175
- pc .sctpTransport .lock .Lock ()
2176
- for _ , d := range pc .sctpTransport .dataChannels {
2177
- closeErrs = append (closeErrs , d .GracefulClose ())
2178
- }
2179
- pc .sctpTransport .lock .Unlock ()
2180
- }
2218
+ closeErrs = append (closeErrs , doGracefulCloseOps ()... )
2181
2219
2182
2220
return util .FlattenErrs (closeErrs )
2183
2221
}
0 commit comments