Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 27f7299

Browse files
authored
chore: Update pion/ice fork to resolve goroutine leak (#78)
* chore: Update pion/ice fork to resolve goroutine leak * Flush remote too * Add logs for setting the description * Try locking only on remote * Remove local bufferring in favor of remote * Remove unused flush func * Set candidates flushed to true * Defer flush until the end of negotiation * Buffer ICE candidates * Add comment clarifying channel buffer * Flush after handshake * Move away from fork * Ignore pion/ice leaks
1 parent 30dae97 commit 27f7299

File tree

4 files changed

+46
-47
lines changed

4 files changed

+46
-47
lines changed

go.mod

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ module github.com/coder/coder
22

33
go 1.17
44

5-
// Required until https://github.com/pion/ice/pull/413 is merged.
6-
replace github.com/pion/ice/v2 => github.com/kylecarbs/ice/v2 v2.1.8-0.20220127013758-526c25708344
7-
85
// Required until https://github.com/hashicorp/terraform-config-inspect/pull/74 is merged.
96
replace github.com/hashicorp/terraform-config-inspect => github.com/kylecarbs/terraform-config-inspect v0.0.0-20211215004401-bbc517866b88
107

go.sum

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,8 +835,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
835835
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
836836
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
837837
github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
838-
github.com/kylecarbs/ice/v2 v2.1.8-0.20220127013758-526c25708344 h1:rXpDqMPlbnKASSBFwPrJbT2wEL5jZzIX/i0cvwISxlM=
839-
github.com/kylecarbs/ice/v2 v2.1.8-0.20220127013758-526c25708344/go.mod h1:E5frMpIJ3zzcQiRo+XyT7z1IiAsGc1hDURcVJQUzGWA=
840838
github.com/kylecarbs/terraform-config-inspect v0.0.0-20211215004401-bbc517866b88 h1:tvG/qs5c4worwGyGnbbb4i/dYYLjpFwDMqcIT3awAf8=
841839
github.com/kylecarbs/terraform-config-inspect v0.0.0-20211215004401-bbc517866b88/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs=
842840
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
@@ -1014,8 +1012,12 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
10141012
github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
10151013
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
10161014
github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ=
1015+
github.com/pion/dtls/v2 v2.0.13/go.mod h1:OaE7eTM+ppaUhJ99OTO4aHl9uY6vPrT1gPY27uNTxRY=
10171016
github.com/pion/dtls/v2 v2.1.0 h1:g6gtKVNLp6URDkv9OijFJl16kqGHzVzZG+Fa4A38GTY=
10181017
github.com/pion/dtls/v2 v2.1.0/go.mod h1:qG3gA7ZPZemBqpEFqRKyURYdKEwFZQCGb7gv9T3ON3Y=
1018+
github.com/pion/ice/v2 v2.1.18/go.mod h1:9jDr0iIUg8P6+0Jq8QJ/eFSkX3JnsPd293TjCdkfpTs=
1019+
github.com/pion/ice/v2 v2.1.19 h1:z7iVx/fHlqvPILUbvcj1xjuz/6eVKgEFOM8h1AuLbF8=
1020+
github.com/pion/ice/v2 v2.1.19/go.mod h1:E5frMpIJ3zzcQiRo+XyT7z1IiAsGc1hDURcVJQUzGWA=
10191021
github.com/pion/interceptor v0.1.6/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U=
10201022
github.com/pion/interceptor v0.1.7 h1:HThW0tIIKT9RRoDWGURe8rlZVOx0fJHxBHpA0ej0+bo=
10211023
github.com/pion/interceptor v0.1.7/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U=
@@ -1311,6 +1313,7 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
13111313
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
13121314
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
13131315
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
1316+
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
13141317
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
13151318
golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8 h1:kACShD3qhmr/3rLmg1yXyt+N4HcwutKyPRB93s54TIU=
13161319
golang.org/x/crypto v0.0.0-20220126234351-aa10faf2a1f8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=

peer/conn.go

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,19 @@ func newWithClientOrServer(servers []webrtc.ICEServer, client bool, opts *ConnOp
6262
return nil, xerrors.Errorf("create peer connection: %w", err)
6363
}
6464
conn := &Conn{
65-
pingChannelID: 1,
66-
pingEchoChannelID: 2,
67-
opts: opts,
68-
rtc: rtc,
69-
offerrer: client,
70-
closed: make(chan struct{}),
71-
dcOpenChannel: make(chan *webrtc.DataChannel),
72-
dcDisconnectChannel: make(chan struct{}),
73-
dcFailedChannel: make(chan struct{}),
74-
localCandidateChannel: make(chan webrtc.ICECandidateInit),
75-
pendingCandidates: make([]webrtc.ICECandidateInit, 0),
65+
pingChannelID: 1,
66+
pingEchoChannelID: 2,
67+
opts: opts,
68+
rtc: rtc,
69+
offerrer: client,
70+
closed: make(chan struct{}),
71+
dcOpenChannel: make(chan *webrtc.DataChannel),
72+
dcDisconnectChannel: make(chan struct{}),
73+
dcFailedChannel: make(chan struct{}),
74+
// This channel needs to be bufferred otherwise slow consumers
75+
// of this will cause a connection failure.
76+
localCandidateChannel: make(chan webrtc.ICECandidateInit, 16),
77+
pendingRemoteCandidates: make([]webrtc.ICECandidateInit, 0),
7678
localSessionDescriptionChannel: make(chan webrtc.SessionDescription),
7779
remoteSessionDescriptionChannel: make(chan webrtc.SessionDescription),
7880
}
@@ -120,7 +122,7 @@ type Conn struct {
120122
localSessionDescriptionChannel chan webrtc.SessionDescription
121123
remoteSessionDescriptionChannel chan webrtc.SessionDescription
122124

123-
pendingCandidates []webrtc.ICECandidateInit
125+
pendingRemoteCandidates []webrtc.ICECandidateInit
124126
pendingCandidatesMutex sync.Mutex
125127
pendingCandidatesFlushed bool
126128

@@ -142,14 +144,6 @@ func (c *Conn) init() error {
142144
if iceCandidate == nil {
143145
return
144146
}
145-
c.pendingCandidatesMutex.Lock()
146-
defer c.pendingCandidatesMutex.Unlock()
147-
148-
if !c.pendingCandidatesFlushed {
149-
c.opts.Logger.Debug(context.Background(), "adding local candidate to buffer")
150-
c.pendingCandidates = append(c.pendingCandidates, iceCandidate.ToJSON())
151-
return
152-
}
153147
c.opts.Logger.Debug(context.Background(), "adding local candidate")
154148
select {
155149
case <-c.closed:
@@ -262,6 +256,7 @@ func (c *Conn) negotiate() {
262256
_ = c.CloseWithError(xerrors.Errorf("create offer: %w", err))
263257
return
264258
}
259+
c.opts.Logger.Debug(context.Background(), "setting local description")
265260
err = c.rtc.SetLocalDescription(offer)
266261
if err != nil {
267262
_ = c.CloseWithError(xerrors.Errorf("set local description: %w", err))
@@ -281,25 +276,20 @@ func (c *Conn) negotiate() {
281276
case remoteDescription = <-c.remoteSessionDescriptionChannel:
282277
}
283278

279+
c.opts.Logger.Debug(context.Background(), "setting remote description")
284280
err := c.rtc.SetRemoteDescription(remoteDescription)
285281
if err != nil {
286-
c.pendingCandidatesMutex.Unlock()
287282
_ = c.CloseWithError(xerrors.Errorf("set remote description (closed %v): %w", c.isClosed(), err))
288283
return
289284
}
290285

291-
if c.offerrer {
292-
// ICE candidates reset when an offer/answer is set for the first
293-
// time. If candidates flush before this point, a connection could fail.
294-
c.flushPendingCandidates()
295-
}
296-
297286
if !c.offerrer {
298287
answer, err := c.rtc.CreateAnswer(&webrtc.AnswerOptions{})
299288
if err != nil {
300289
_ = c.CloseWithError(xerrors.Errorf("create answer: %w", err))
301290
return
302291
}
292+
c.opts.Logger.Debug(context.Background(), "setting local description")
303293
err = c.rtc.SetLocalDescription(answer)
304294
if err != nil {
305295
_ = c.CloseWithError(xerrors.Errorf("set local description: %w", err))
@@ -313,28 +303,23 @@ func (c *Conn) negotiate() {
313303
return
314304
case c.localSessionDescriptionChannel <- answer:
315305
}
316-
317-
// Wait until the local description is set to flush candidates.
318-
c.flushPendingCandidates()
319306
}
320-
}
321307

322-
// flushPendingCandidates writes all local candidates to the candidate send channel.
323-
// The localCandidateChannel is expected to be serviced, otherwise this could block.
324-
func (c *Conn) flushPendingCandidates() {
308+
// The ICE transport resets when the remote description is updated.
309+
// Adding ICE candidates before this point causes a failed connection,
310+
// because the candidate would be lost.
325311
c.pendingCandidatesMutex.Lock()
326312
defer c.pendingCandidatesMutex.Unlock()
327-
for _, pendingCandidate := range c.pendingCandidates {
328-
c.opts.Logger.Debug(context.Background(), "flushing local candidate")
329-
select {
330-
case <-c.closed:
313+
for _, pendingCandidate := range c.pendingRemoteCandidates {
314+
c.opts.Logger.Debug(context.Background(), "flushing remote candidate")
315+
err := c.rtc.AddICECandidate(pendingCandidate)
316+
if err != nil {
317+
_ = c.CloseWithError(xerrors.Errorf("flush pending candidates: %w", err))
331318
return
332-
case c.localCandidateChannel <- pendingCandidate:
333319
}
334320
}
335-
c.pendingCandidates = make([]webrtc.ICECandidateInit, 0)
336321
c.pendingCandidatesFlushed = true
337-
c.opts.Logger.Debug(context.Background(), "flushed candidates")
322+
c.opts.Logger.Debug(context.Background(), "flushed remote candidates")
338323
}
339324

340325
// LocalCandidate returns a channel that emits when a local candidate
@@ -345,6 +330,13 @@ func (c *Conn) LocalCandidate() <-chan webrtc.ICECandidateInit {
345330

346331
// AddRemoteCandidate adds a remote candidate to the RTC connection.
347332
func (c *Conn) AddRemoteCandidate(i webrtc.ICECandidateInit) error {
333+
c.pendingCandidatesMutex.Lock()
334+
defer c.pendingCandidatesMutex.Unlock()
335+
if !c.pendingCandidatesFlushed {
336+
c.opts.Logger.Debug(context.Background(), "adding remote candidate to buffer")
337+
c.pendingRemoteCandidates = append(c.pendingRemoteCandidates, i)
338+
return nil
339+
}
348340
c.opts.Logger.Debug(context.Background(), "adding remote candidate")
349341
return c.rtc.AddICECandidate(i)
350342
}

peer/conn_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@ var (
4848
)
4949

5050
func TestMain(m *testing.M) {
51-
goleak.VerifyTestMain(m)
51+
// pion/ice doesn't properly close immediately. The solution for this isn't yet known. See:
52+
// https://github.com/pion/ice/pull/413
53+
goleak.VerifyTestMain(m,
54+
goleak.IgnoreTopFunction("github.com/pion/ice/v2.(*Agent).startOnConnectionStateChangeRoutine.func1"),
55+
goleak.IgnoreTopFunction("github.com/pion/ice/v2.(*Agent).startOnConnectionStateChangeRoutine.func2"),
56+
goleak.IgnoreTopFunction("github.com/pion/ice/v2.(*Agent).taskLoop"),
57+
goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"),
58+
)
5259
}
5360

5461
func TestConn(t *testing.T) {

0 commit comments

Comments
 (0)