Skip to content

Commit b3856ff

Browse files
committed
Make onNegotiationNeeded conform to spec
- Removes non-canon logic
1 parent 4430f41 commit b3856ff

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
@@ -115,6 +115,7 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
115115
// https://w3c.github.io/webrtc-pc/#constructor (Step #2)
116116
// Some variables defined explicitly despite their implicit zero values to
117117
// allow better readability to understand what is happening.
118+
118119
pc := &PeerConnection{
119120
statsID: fmt.Sprintf("PeerConnection-%d", time.Now().UnixNano()),
120121
configuration: Configuration{
@@ -125,18 +126,19 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection,
125126
Certificates: []Certificate{},
126127
ICECandidatePoolSize: 0,
127128
},
128-
ops: newOperations(),
129-
isClosed: &atomicBool{},
130-
isNegotiationNeeded: &atomicBool{},
131-
negotiationNeededState: negotiationNeededStateEmpty,
132-
lastOffer: "",
133-
lastAnswer: "",
134-
greaterMid: -1,
135-
signalingState: SignalingStateStable,
129+
isClosed: &atomicBool{},
130+
isNegotiationNeeded: &atomicBool{},
131+
updateNegotiationNeededFlagOnEmptyChain: &atomicBool{},
132+
lastOffer: "",
133+
lastAnswer: "",
134+
greaterMid: -1,
135+
signalingState: SignalingStateStable,
136136

137137
api: api,
138138
log: api.settingEngine.LoggerFactory.NewLogger("pc"),
139139
}
140+
pc.ops = newOperations(pc.updateNegotiationNeededFlagOnEmptyChain, pc.onNegotiationNeeded)
141+
140142
pc.iceConnectionState.Store(ICEConnectionStateNew)
141143
pc.connectionState.Store(PeerConnectionStateNew)
142144

@@ -293,66 +295,54 @@ func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
293295

294296
// onNegotiationNeeded enqueues negotiationNeededOp if necessary
295297
// caller of this method should hold `pc.mu` lock
298+
// https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag
296299
func (pc *PeerConnection) onNegotiationNeeded() {
297-
// https://w3c.github.io/webrtc-pc/#updating-the-negotiation-needed-flag
298-
// non-canon step 1
299-
if pc.negotiationNeededState == negotiationNeededStateRun {
300-
pc.negotiationNeededState = negotiationNeededStateQueue
301-
return
302-
} else if pc.negotiationNeededState == negotiationNeededStateQueue {
300+
// 4.7.3.1 If the length of connection.[[Operations]] is not 0, then set
301+
// connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps.
302+
if !pc.ops.IsEmpty() {
303+
pc.updateNegotiationNeededFlagOnEmptyChain.set(true)
303304
return
304305
}
305-
pc.negotiationNeededState = negotiationNeededStateRun
306306
pc.ops.Enqueue(pc.negotiationNeededOp)
307307
}
308308

309+
// https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag
309310
func (pc *PeerConnection) negotiationNeededOp() {
310-
// non-canon, reset needed state machine and run again if there was a request
311-
defer func() {
312-
pc.mu.Lock()
313-
defer pc.mu.Unlock()
314-
if pc.negotiationNeededState == negotiationNeededStateQueue {
315-
defer pc.onNegotiationNeeded()
316-
}
317-
pc.negotiationNeededState = negotiationNeededStateEmpty
318-
}()
319-
320-
// Don't run NegotiatedNeeded checks if OnNegotiationNeeded is not set
321-
if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); !ok || handler == nil {
322-
return
323-
}
324-
325-
// https://www.w3.org/TR/webrtc/#updating-the-negotiation-needed-flag
326-
// Step 2.1
311+
// 4.7.3.2.1 If connection.[[IsClosed]] is true, abort these steps.
327312
if pc.isClosed.get() {
328313
return
329314
}
330-
// non-canon step 2.2
315+
316+
// 4.7.3.2.2 If the length of connection.[[Operations]] is not 0,
317+
// then set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to
318+
// true, and abort these steps.
331319
if !pc.ops.IsEmpty() {
332-
pc.ops.Enqueue(pc.negotiationNeededOp)
320+
pc.updateNegotiationNeededFlagOnEmptyChain.set(true)
333321
return
334322
}
335323

336-
// Step 2.3
324+
// 4.7.3.2.3 If connection's signaling state is not "stable", abort these steps.
337325
if pc.SignalingState() != SignalingStateStable {
338326
return
339327
}
340328

341-
// Step 2.4
329+
// 4.7.3.2.4 If the result of checking if negotiation is needed is false,
330+
// clear the negotiation-needed flag by setting connection.[[NegotiationNeeded]]
331+
// to false, and abort these steps.
342332
if !pc.checkNegotiationNeeded() {
343333
pc.isNegotiationNeeded.set(false)
344334
return
345335
}
346336

347-
// Step 2.5
337+
// 4.7.3.2.5 If connection.[[NegotiationNeeded]] is already true, abort these steps.
348338
if pc.isNegotiationNeeded.get() {
349339
return
350340
}
351341

352-
// Step 2.6
342+
// 4.7.3.2.6 Set connection.[[NegotiationNeeded]] to true.
353343
pc.isNegotiationNeeded.set(true)
354344

355-
// Step 2.7
345+
// 4.7.3.2.7 Fire an event named negotiationneeded at connection.
356346
if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); ok && handler != nil {
357347
handler()
358348
}

peerconnection_go_test.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,41 +1623,3 @@ func TestPeerConnectionState(t *testing.T) {
16231623
assert.NoError(t, pc.Close())
16241624
assert.Equal(t, PeerConnectionStateClosed, pc.ConnectionState())
16251625
}
1626-
1627-
// See https://github.com/pion/webrtc/issues/2774
1628-
func TestNegotiationNeededAddedAfterOpQueueDone(t *testing.T) {
1629-
lim := test.TimeOut(time.Second * 30)
1630-
defer lim.Stop()
1631-
1632-
report := test.CheckRoutines(t)
1633-
defer report()
1634-
1635-
pc, err := NewPeerConnection(Configuration{})
1636-
if err != nil {
1637-
t.Error(err.Error())
1638-
}
1639-
1640-
var wg sync.WaitGroup
1641-
wg.Add(1)
1642-
1643-
_, err = pc.CreateDataChannel("initial_data_channel", nil)
1644-
assert.NoError(t, err)
1645-
1646-
// after there are no ops left in the queue, a previously faulty version
1647-
// of negotiationNeededOp would keep the negotiation needed state in
1648-
// negotiationNeededStateQueue which will cause all subsequent
1649-
// onNegotiationNeeded calls to never queue again, only if
1650-
// OnNegotiationNeeded has not been set yet.
1651-
for !pc.ops.IsEmpty() {
1652-
time.Sleep(time.Millisecond)
1653-
}
1654-
1655-
pc.OnNegotiationNeeded(wg.Done)
1656-
1657-
_, err = pc.CreateDataChannel("another_data_channel", nil)
1658-
assert.NoError(t, err)
1659-
1660-
wg.Wait()
1661-
1662-
assert.NoError(t, pc.Close())
1663-
}

peerconnectionstate.go

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

0 commit comments

Comments
 (0)