Optimize Read by buffering

Increases the Read performance to acceptable levels. Packet loss went
from roughly 8% to 0.5% with this change.
This commit is contained in:
Luke Curley
2019-03-12 21:34:48 -07:00
committed by kixelated
parent 052b3f98c6
commit 70092d3a81
3 changed files with 25 additions and 25 deletions
+16 -8
View File
@@ -13,6 +13,7 @@ import (
"errors"
"github.com/pions/stun"
"github.com/pions/transport/packetio"
"github.com/pions/webrtc/internal/util"
)
@@ -25,6 +26,9 @@ const (
// defaultConnectionTimeout used to declare a connection dead
defaultConnectionTimeout = 30 * time.Second
// the number of bytes that can be buffered before we start to error
maxBufferSize = 1000 * 1000 // 1MB
)
// Agent represents the ICE agent
@@ -72,19 +76,13 @@ type Agent struct {
selectedPair *candidatePair
validPairs []*candidatePair
// Channel for reading
rcvCh chan *bufIn
buffer *packetio.Buffer
// State for closing
done chan struct{}
err atomicError
}
type bufIn struct {
buf []byte
size chan int
}
func (a *Agent) ok() error {
select {
case <-a.done:
@@ -142,12 +140,17 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
localPwd: util.RandSeq(32),
taskChan: make(chan task),
onConnected: make(chan struct{}),
rcvCh: make(chan *bufIn),
buffer: packetio.NewBuffer(),
done: make(chan struct{}),
portmin: config.PortMin,
portmax: config.PortMax,
}
// Make sure the buffer doesn't grow indefinitely.
// NOTE: We actually won't get anywhere close to this limit.
// SRTP will constantly read from the endpoint and drop packets if it's full.
a.buffer.SetLimitSize(maxBufferSize)
// connectionTimeout used to declare a connection dead
if config.ConnectionTimeout == nil {
a.connectionTimeout = defaultConnectionTimeout
@@ -595,6 +598,11 @@ func (a *Agent) Close() error {
}
delete(agent.remoteCandidates, net)
}
err := a.buffer.Close()
if err != nil {
iceLog.Warnf("failed to close buffer: %v", err)
}
})
if err != nil {
return err
+7 -8
View File
@@ -140,7 +140,8 @@ func (c *Candidate) recvLoop() {
}
if stun.IsSTUN(buffer[:n]) {
m, err := stun.NewMessage(buffer[:n])
var m *stun.Message
m, err = stun.NewMessage(buffer[:n])
if err != nil {
iceLog.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err)
continue
@@ -154,7 +155,7 @@ func (c *Candidate) recvLoop() {
continue
} else {
err := c.agent.run(func(agent *Agent) {
err = c.agent.run(func(agent *Agent) {
agent.noSTUNSeen(c, srcAddr)
})
if err != nil {
@@ -162,12 +163,10 @@ func (c *Candidate) recvLoop() {
}
}
select {
case bufin := <-c.agent.rcvCh:
copy(bufin.buf, buffer[:n]) // TODO: avoid copy in common case?
bufin.size <- n
case <-c.closeCh:
return
// NOTE This will return packetio.ErrFull if the buffer ever manages to fill up.
_, err = c.agent.buffer.Write(buffer[:n])
if err != nil {
iceLog.Warnf("failed to write packet")
}
}
}
+2 -9
View File
@@ -61,15 +61,7 @@ func (c *Conn) Read(p []byte) (int, error) {
return 0, err
}
resN := make(chan int)
select {
case c.agent.rcvCh <- &bufIn{p, resN}:
n := <-resN
return n, nil
case <-c.agent.done:
return 0, c.agent.getErr()
}
return c.agent.buffer.Read(p)
}
// Write implements the Conn Write method.
@@ -87,6 +79,7 @@ func (c *Conn) Write(p []byte) (int, error) {
if err != nil {
return 0, err
}
return pair.Write(p)
}