Send connectivity checks right on agent start

Connectivity checks are done via a Ticker, the inital tick does not
happen immediately (by design) causing the startup time to be the
Duration the Ticker is created with.

This change adds another chan forceCandidateContact that can be used to
force contact at anytime. Currently it is only called on startup, but
could be useful in the future for reuse.

Resolve #15
This commit is contained in:
Sean DuBois
2019-04-13 11:31:04 -07:00
parent ad7c8697fd
commit f58e725a2c
3 changed files with 78 additions and 17 deletions
+38 -16
View File
@@ -19,7 +19,7 @@ import (
const (
// taskLoopInterval is the interval at which the agent performs checks
taskLoopInterval = 2 * time.Second
defaultTaskLoopInterval = 2 * time.Second
// keepaliveInterval used to keep candidates alive
defaultKeepaliveInterval = 10 * time.Second
@@ -44,8 +44,9 @@ type Agent struct {
onConnected chan struct{}
onConnectedOnce sync.Once
connectivityTicker *time.Ticker
connectivityChan <-chan time.Time
connectivityChan <-chan time.Time
// force candidate to be contacted immediately (instead of waiting for connectivityChan)
forceCandidateContact chan bool
tieBreaker uint64
connectionState ConnectionState
@@ -65,6 +66,9 @@ type Agent struct {
//0 means never
keepaliveInterval time.Duration
// How after should we run our internal taskLoop
taskLoopInterval time.Duration
localUfrag string
localPwd string
localCandidates map[NetworkType][]*Candidate
@@ -125,6 +129,11 @@ type AgentConfig struct {
NetworkTypes []NetworkType
LoggerFactory logging.LoggerFactory
// taskLoopInterval controls how often our internal task loop runs, this
// task loop handles things like sending keepAlives. This is only value for testing
// keepAlive behavior should be modified with KeepaliveInterval and ConnectionTimeout
taskLoopInterval time.Duration
}
// NewAgent creates a new Agent
@@ -154,6 +163,8 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
portmin: config.PortMin,
portmax: config.PortMax,
log: loggerFactory.NewLogger("ice"),
forceCandidateContact: make(chan bool, 1),
}
// Make sure the buffer doesn't grow indefinitely.
@@ -174,6 +185,12 @@ func NewAgent(config *AgentConfig) (*Agent, error) {
a.keepaliveInterval = *config.KeepaliveInterval
}
if config.taskLoopInterval == 0 {
a.taskLoopInterval = defaultTaskLoopInterval
} else {
a.taskLoopInterval = config.taskLoopInterval
}
// Initialize local candidates
a.gatherCandidatesLocal(config.NetworkTypes)
a.gatherCandidatesReflective(config.Urls, config.NetworkTypes)
@@ -345,12 +362,12 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP
agent.remoteUfrag = remoteUfrag
agent.remotePwd = remotePwd
// TODO this should be dynamic, and grow when the connection is stable
t := time.NewTicker(taskLoopInterval)
agent.connectivityTicker = t
agent.connectivityChan = t.C
agent.updateConnectionState(ConnectionStateChecking)
// TODO this should be dynamic, and grow when the connection is stable
agent.forceCandidateContact <- true
t := time.NewTicker(a.taskLoopInterval)
agent.connectivityChan = t.C
})
}
@@ -465,17 +482,22 @@ func (a *Agent) run(t task) error {
}
func (a *Agent) taskLoop() {
contactCandidates := func() {
if a.validateSelectedPair() {
a.log.Trace("checking keepalive")
a.checkKeepalive()
} else {
a.log.Trace("pinging all candidates")
a.pingAllCandidates()
}
}
for {
select {
case <-a.forceCandidateContact:
contactCandidates()
case <-a.connectivityChan:
if a.validateSelectedPair() {
a.log.Trace("checking keepalive")
a.checkKeepalive()
} else {
a.log.Trace("pinging all candidates")
a.pingAllCandidates()
}
contactCandidates()
case t := <-a.taskChan:
// Run the task
t(a)
+39
View File
@@ -312,3 +312,42 @@ func TestHandlePeerReflexive(t *testing.T) {
}
})
}
// Assert that Agent on startup sends message, and doesn't wait for taskloop to start
// github.com/pion/ice/issues/15
func TestConnectivityOnStartup(t *testing.T) {
lim := test.TimeOut(time.Second * 5)
defer lim.Stop()
cfg := &AgentConfig{
Urls: []*URL{},
NetworkTypes: supportedNetworkTypes,
taskLoopInterval: time.Hour,
}
aNotifier, aConnected := onConnected()
bNotifier, bConnected := onConnected()
aAgent, err := NewAgent(cfg)
if err != nil {
t.Error(err)
}
err = aAgent.OnConnectionStateChange(aNotifier)
if err != nil {
panic(err)
}
bAgent, err := NewAgent(cfg)
if err != nil {
t.Error(err)
}
err = bAgent.OnConnectionStateChange(bNotifier)
if err != nil {
panic(err)
}
connect(aAgent, bAgent)
<-aConnected
<-bConnected
}
+1 -1
View File
@@ -26,7 +26,7 @@ func testTimeout(t *testing.T, c *Conn, timeout time.Duration) {
statechan := make(chan ConnectionState)
ticker := time.NewTicker(pollrate)
for cnt := time.Duration(0); cnt <= timeout+taskLoopInterval; cnt += pollrate {
for cnt := time.Duration(0); cnt <= timeout+defaultTaskLoopInterval; cnt += pollrate {
<-ticker.C
err := c.agent.run(func(agent *Agent) {
statechan <- agent.connectionState