Skip to content

Commit 1bb9fa9

Browse files
committed
Make onNegotiationNeeded conform to spec
- Removes non-canon logic
1 parent d56bead commit 1bb9fa9

File tree

5 files changed

+66
-96
lines changed

5 files changed

+66
-96
lines changed

operations.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@ type operations struct {
1616
mu sync.Mutex
1717
busy bool
1818
ops *list.List
19+
20+
updateNegotiationNeededFlagOnEmptyChain *atomicBool
21+
onNegotiationNeeded func()
1922
}
2023

21-
func newOperations() *operations {
24+
func newOperations(
25+
updateNegotiationNeededFlagOnEmptyChain *atomicBool,
26+
onNegotiationNeeded func(),
27+
) *operations {
2228
return &operations{
23-
ops: list.New(),
29+
ops: list.New(),
30+
updateNegotiationNeededFlagOnEmptyChain: updateNegotiationNeededFlagOnEmptyChain,
31+
onNegotiationNeeded: onNegotiationNeeded,
2432
}
2533
}
2634

@@ -93,4 +101,9 @@ func (o *operations) start() {
93101
fn()
94102
fn = o.pop()
95103
}
104+
if !o.updateNegotiationNeededFlagOnEmptyChain.get() {
105+
return
106+
}
107+
o.updateNegotiationNeededFlagOnEmptyChain.set(false)
108+
o.onNegotiationNeeded()
96109
}

operations_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,31 @@
44
package webrtc
55

66
import (
7+
"sync"
78
"testing"
89

910
"github.com/stretchr/testify/assert"
1011
)
1112

1213
func TestOperations_Enqueue(t *testing.T) {
13-
ops := newOperations()
14-
for i := 0; i < 100; i++ {
14+
updateNegotiationNeededFlagOnEmptyChain := &atomicBool{}
15+
onNegotiationNeededCalledCount := 0
16+
var onNegotiationNeededCalledCountMu sync.Mutex
17+
ops := newOperations(updateNegotiationNeededFlagOnEmptyChain, func() {
18+
onNegotiationNeededCalledCountMu.Lock()
19+
onNegotiationNeededCalledCount++
20+
onNegotiationNeededCalledCountMu.Unlock()
21+
})
22+
for resultSet := 0; resultSet < 100; resultSet++ {
1523
results := make([]int, 16)
24+
resultSetCopy := resultSet
1625
for i := range results {
1726
func(j int) {
1827
ops.Enqueue(func() {
1928
results[j] = j * j
29+
if resultSetCopy > 50 {
30+
updateNegotiationNeededFlagOnEmptyChain.set(true)
31+
}
2032
})
2133
}(i)
2234
}
@@ -26,9 +38,13 @@ func TestOperations_Enqueue(t *testing.T) {
2638
assert.Equal(t, len(expected), len(results))
2739
assert.Equal(t, expected, results)
2840
}
41+
onNegotiationNeededCalledCountMu.Lock()
42+
defer onNegotiationNeededCalledCountMu.Unlock()
43+
assert.NotEqual(t, onNegotiationNeededCalledCount, 0)
2944
}
3045

3146
func TestOperations_Done(*testing.T) {
32-
ops := newOperations()
47+
ops := newOperations(&atomicBool{}, func() {
48+
})
3349
ops.Done()
3450
}

peerconnection.go

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ type PeerConnection struct {
5555

5656
idpLoginURL *string
5757

58-
isClosed *atomicBool
59-
isNegotiationNeeded *atomicBool
60-
negotiationNeededState negotiationNeededState
58+
isClosed *atomicBool
59+
isNegotiationNeeded *atomicBool
60+
updateNegotiationNeededFlagOnEmptyChain *atomicBool
6161

6262
lastOffer string
6363
lastAnswer string
@@ -104,6 +104,7 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
104104
// https://w3c.github.io/webrtc-pc/#constructor (Step #2)
105105
// Some variables defined explicitly despite their implicit zero values to
106106
// allow better readability to understand what is happening.
107+
107108
pc := &PeerConnection{
108109
statsID: fmt.Sprintf("PeerConnection-%d", time.Now().UnixNano()),
109110
configuration: Configuration{
@@ -114,18 +115,19 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
114115
Certificates: []Certificate{},
115116
ICECandidatePoolSize: 0,
116117
},
117-
ops: newOperations(),
118-
isClosed: &atomicBool{},
119-
isNegotiationNeeded: &atomicBool{},
120-
negotiationNeededState: negotiationNeededStateEmpty,
121-
lastOffer: "",
122-
lastAnswer: "",
123-
greaterMid: -1,
124-
signalingState: SignalingStateStable,
118+
isClosed: &atomicBool{},
119+
isNegotiationNeeded: &atomicBool{},
120+
updateNegotiationNeededFlagOnEmptyChain: &atomicBool{},
121+
lastOffer: "",
122+
lastAnswer: "",
123+
greaterMid: -1,
124+
signalingState: SignalingStateStable,
125125

126126
api: api,
127127
log: api.settingEngine.LoggerFactory.NewLogger("pc"),
128128
}
129+
pc.ops = newOperations(pc.updateNegotiationNeededFlagOnEmptyChain, pc.onNegotiationNeeded)
130+
129131
pc.iceConnectionState.Store(ICEConnectionStateNew)
130132
pc.connectionState.Store(PeerConnectionStateNew)
131133

@@ -277,66 +279,54 @@ func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
277279

278280
// onNegotiationNeeded enqueues negotiationNeededOp if necessary
279281
// caller of this method should hold `pc.mu` lock
282+
// https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag
280283
func (pc *PeerConnection) onNegotiationNeeded() {
281-
// https://w3c.github.io/webrtc-pc/#updating-the-negotiation-needed-flag
282-
// non-canon step 1
283-
if pc.negotiationNeededState == negotiationNeededStateRun {
284-
pc.negotiationNeededState = negotiationNeededStateQueue
285-
return
286-
} else if pc.negotiationNeededState == negotiationNeededStateQueue {
284+
// 4.7.3.1 If the length of connection.[[Operations]] is not 0, then set
285+
// connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps.
286+
if !pc.ops.IsEmpty() {
287+
pc.updateNegotiationNeededFlagOnEmptyChain.set(true)
287288
return
288289
}
289-
pc.negotiationNeededState = negotiationNeededStateRun
290290
pc.ops.Enqueue(pc.negotiationNeededOp)
291291
}
292292

293+
// https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag
293294
func (pc *PeerConnection) negotiationNeededOp() {
294-
// non-canon, reset needed state machine and run again if there was a request
295-
defer func() {
296-
pc.mu.Lock()
297-
defer pc.mu.Unlock()
298-
if pc.negotiationNeededState == negotiationNeededStateQueue {
299-
defer pc.onNegotiationNeeded()
300-
}
301-
pc.negotiationNeededState = negotiationNeededStateEmpty
302-
}()
303-
304-
// Don't run NegotiatedNeeded checks if OnNegotiationNeeded is not set
305-
if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); !ok || handler == nil {
306-
return
307-
}
308-
309-
// https://www.w3.org/TR/webrtc/#updating-the-negotiation-needed-flag
310-
// Step 2.1
295+
// 4.7.3.2.1 If connection.[[IsClosed]] is true, abort these steps.
311296
if pc.isClosed.get() {
312297
return
313298
}
314-
// non-canon step 2.2
299+
300+
// 4.7.3.2.2 If the length of connection.[[Operations]] is not 0,
301+
// then set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to
302+
// true, and abort these steps.
315303
if !pc.ops.IsEmpty() {
316-
pc.ops.Enqueue(pc.negotiationNeededOp)
304+
pc.updateNegotiationNeededFlagOnEmptyChain.set(true)
317305
return
318306
}
319307

320-
// Step 2.3
308+
// 4.7.3.2.3 If connection's signaling state is not "stable", abort these steps.
321309
if pc.SignalingState() != SignalingStateStable {
322310
return
323311
}
324312

325-
// Step 2.4
313+
// 4.7.3.2.4 If the result of checking if negotiation is needed is false,
314+
// clear the negotiation-needed flag by setting connection.[[NegotiationNeeded]]
315+
// to false, and abort these steps.
326316
if !pc.checkNegotiationNeeded() {
327317
pc.isNegotiationNeeded.set(false)
328318
return
329319
}
330320

331-
// Step 2.5
321+
// 4.7.3.2.5 If connection.[[NegotiationNeeded]] is already true, abort these steps.
332322
if pc.isNegotiationNeeded.get() {
333323
return
334324
}
335325

336-
// Step 2.6
326+
// 4.7.3.2.6 Set connection.[[NegotiationNeeded]] to true.
337327
pc.isNegotiationNeeded.set(true)
338328

339-
// Step 2.7
329+
// 4.7.3.2.7 Fire an event named negotiationneeded at connection.
340330
if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); ok && handler != nil {
341331
handler()
342332
}

peerconnection_go_test.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,41 +1606,3 @@ func TestPeerConnectionState(t *testing.T) {
16061606
assert.NoError(t, pc.Close())
16071607
assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState())
16081608
}
1609-
1610-
// See https://github.com/pion/webrtc/issues/2774
1611-
func TestNegotiationNeededAddedAfterOpQueueDone(t *testing.T) {
1612-
lim := test.TimeOut(time.Second * 30)
1613-
defer lim.Stop()
1614-
1615-
report := test.CheckRoutines(t)
1616-
defer report()
1617-
1618-
pc, err := NewPeerConnection(Configuration{})
1619-
if err != nil {
1620-
t.Error(err.Error())
1621-
}
1622-
1623-
var wg sync.WaitGroup
1624-
wg.Add(1)
1625-
1626-
_, err = pc.CreateDataChannel("initial_data_channel", nil)
1627-
assert.NoError(t, err)
1628-
1629-
// after there are no ops left in the queue, a previously faulty version
1630-
// of negotiationNeededOp would keep the negotiation needed state in
1631-
// negotiationNeededStateQueue which will cause all subsequent
1632-
// onNegotiationNeeded calls to never queue again, only if
1633-
// OnNegotiationNeeded has not been set yet.
1634-
for !pc.ops.IsEmpty() {
1635-
time.Sleep(time.Millisecond)
1636-
}
1637-
1638-
pc.OnNegotiationNeeded(wg.Done)
1639-
1640-
_, err = pc.CreateDataChannel("another_data_channel", nil)
1641-
assert.NoError(t, err)
1642-
1643-
wg.Wait()
1644-
1645-
assert.NoError(t, pc.Close())
1646-
}

peerconnectionstate.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,3 @@ func (t PeerConnectionState) String() string {
8787
return ErrUnknownType.Error()
8888
}
8989
}
90-
91-
type negotiationNeededState int
92-
93-
const (
94-
// NegotiationNeededStateEmpty not running and queue is empty
95-
negotiationNeededStateEmpty = iota
96-
// NegotiationNeededStateEmpty running and queue is empty
97-
negotiationNeededStateRun
98-
// NegotiationNeededStateEmpty running and queue
99-
negotiationNeededStateQueue
100-
)

0 commit comments

Comments
 (0)