@@ -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 : "" ,
@@ -2087,22 +2088,44 @@ func (pc *PeerConnection) GracefulClose() error {
2087
2088
func (pc * PeerConnection ) close (shouldGracefullyClose bool ) error {
2088
2089
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #1)
2089
2090
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2)
2090
- alreadyGracefullyClosed := shouldGracefullyClose && pc .isGracefulClosed .swap (true )
2091
- if pc .isClosed .swap (true ) {
2092
- if alreadyGracefullyClosed {
2093
- // similar but distinct condition where we may be waiting for some
2094
- // other graceful close to finish. Incorrectly using isClosed may
2095
- // leak a goroutine.
2096
- <- pc .isGracefulClosedDone
2097
- }
2098
- return nil
2091
+
2092
+ pc .mu .Lock ()
2093
+ // A lock in this critical section is needed because pc.isClosed and
2094
+ // pc.isGracefullyClosingOrClosed are related to each other in that we
2095
+ // want to make graceful and normal closure one time operations in order
2096
+ // to avoid any double closure errors from cropping up. However, there are
2097
+ // some overlapping close cases when both normal and graceful close are used
2098
+ // that should be idempotent, but be cautioned when writing new close behavior
2099
+ // to preserve this property.
2100
+ isAlreadyClosingOrClosed := pc .isClosed .swap (true )
2101
+ isAlreadyGracefullyClosingOrClosed := pc .isGracefullyClosingOrClosed
2102
+ if shouldGracefullyClose && ! isAlreadyGracefullyClosingOrClosed {
2103
+ pc .isGracefullyClosingOrClosed = true
2099
2104
}
2100
- if shouldGracefullyClose && ! alreadyGracefullyClosed {
2101
- defer close (pc .isGracefulClosedDone )
2105
+ pc .mu .Unlock ()
2106
+
2107
+ if isAlreadyClosingOrClosed {
2108
+ if ! shouldGracefullyClose {
2109
+ return nil
2110
+ }
2111
+ // Even if we're already closing, it may not be graceful:
2112
+ // If we are not the ones doing the closing, we just wait for the graceful close
2113
+ // to happen and then return.
2114
+ if isAlreadyGracefullyClosingOrClosed {
2115
+ <- pc .isGracefulCloseDone
2116
+ return nil
2117
+ }
2118
+ // Otherwise we need to go through the graceful closure flow once the
2119
+ // normal closure is done since there are extra steps to take with a
2120
+ // graceful close.
2121
+ <- pc .isCloseDone
2122
+ } else {
2123
+ defer close (pc .isCloseDone )
2102
2124
}
2103
2125
2104
- // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
2105
- pc .signalingState .Set (SignalingStateClosed )
2126
+ if shouldGracefullyClose {
2127
+ defer close (pc .isGracefulCloseDone )
2128
+ }
2106
2129
2107
2130
// Try closing everything and collect the errors
2108
2131
// Shutdown strategy:
@@ -2112,6 +2135,34 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error {
2112
2135
// continue the chain the Mux has to be closed.
2113
2136
closeErrs := make ([]error , 4 )
2114
2137
2138
+ doGracefulCloseOps := func () []error {
2139
+ if ! shouldGracefullyClose {
2140
+ return nil
2141
+ }
2142
+
2143
+ // these are all non-canon steps
2144
+ var gracefulCloseErrors []error
2145
+ if pc .iceTransport != nil {
2146
+ gracefulCloseErrors = append (gracefulCloseErrors , pc .iceTransport .GracefulStop ())
2147
+ }
2148
+
2149
+ pc .ops .GracefulClose ()
2150
+
2151
+ pc .sctpTransport .lock .Lock ()
2152
+ for _ , d := range pc .sctpTransport .dataChannels {
2153
+ gracefulCloseErrors = append (gracefulCloseErrors , d .GracefulClose ())
2154
+ }
2155
+ pc .sctpTransport .lock .Unlock ()
2156
+ return gracefulCloseErrors
2157
+ }
2158
+
2159
+ if isAlreadyClosingOrClosed {
2160
+ return util .FlattenErrs (doGracefulCloseOps ())
2161
+ }
2162
+
2163
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
2164
+ pc .signalingState .Set (SignalingStateClosed )
2165
+
2115
2166
closeErrs = append (closeErrs , pc .api .interceptor .Close ())
2116
2167
2117
2168
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #4)
@@ -2142,28 +2193,15 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error {
2142
2193
closeErrs = append (closeErrs , pc .dtlsTransport .Stop ())
2143
2194
2144
2195
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #8, #9, #10)
2145
- if pc .iceTransport != nil {
2146
- if shouldGracefullyClose {
2147
- // note that it isn't canon to stop gracefully
2148
- closeErrs = append (closeErrs , pc .iceTransport .GracefulStop ())
2149
- } else {
2150
- closeErrs = append (closeErrs , pc .iceTransport .Stop ())
2151
- }
2196
+ if pc .iceTransport != nil && ! shouldGracefullyClose {
2197
+ // we will stop gracefully in doGracefulCloseOps
2198
+ closeErrs = append (closeErrs , pc .iceTransport .Stop ())
2152
2199
}
2153
2200
2154
2201
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11)
2155
2202
pc .updateConnectionState (pc .ICEConnectionState (), pc .dtlsTransport .State ())
2156
2203
2157
- if shouldGracefullyClose {
2158
- pc .ops .GracefulClose ()
2159
-
2160
- // note that it isn't canon to stop gracefully
2161
- pc .sctpTransport .lock .Lock ()
2162
- for _ , d := range pc .sctpTransport .dataChannels {
2163
- closeErrs = append (closeErrs , d .GracefulClose ())
2164
- }
2165
- pc .sctpTransport .lock .Unlock ()
2166
- }
2204
+ closeErrs = append (closeErrs , doGracefulCloseOps ()... )
2167
2205
2168
2206
return util .FlattenErrs (closeErrs )
2169
2207
}
0 commit comments