monitor: refactor for architecture-specific connection implementations

Signed-off-by: wardn <wardn@users.noreply.github.com>
This commit is contained in:
wardn
2020-02-12 12:53:55 -08:00
committed by Dave Anderson
parent aa73b7972c
commit 2cafbd74c7
5 changed files with 216 additions and 119 deletions

106
wgengine/monitor/monitor.go Normal file
View File

@@ -0,0 +1,106 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package monitor provides facilities for monitoring network
// interface changes.
package monitor
import (
"time"
"tailscale.com/logger"
)
// Message represents a message returned from a connection.
// TODO(]|[): currently messages are being discarded, so the
// properties of the message haven't been defined.
type Message interface{}
// Conn represents the connection that is being monitored.
type Conn interface {
Close() error
Receive() (Message, error)
}
// ChangeFunc is a callback function that's called when
// an interface status changes.
type ChangeFunc func()
// Mon represents a monitoring instance.
type Mon struct {
logf logger.Logf
cb ChangeFunc
conn Conn
change chan struct{}
stop chan struct{}
}
// New instantiates and starts a monitoring instance. Change notifications
// are propagated to the callback function.
func New(logf logger.Logf, callback ChangeFunc) (*Mon, error) {
conn, err := NewConn()
if err != nil {
return nil, err
}
ret := &Mon{
logf: logf,
cb: callback,
conn: conn,
change: make(chan struct{}, 1),
stop: make(chan struct{}),
}
go ret.pump()
go ret.debounce()
return ret, nil
}
// Close is used to close the underlying connection.
func (m *Mon) Close() error {
close(m.stop)
return m.conn.Close()
}
// pump continuously retrieves messages from the connection, notifying
// the change channel of changes, and stopping when a stop is issued.
func (m *Mon) pump() {
for {
_, err := m.conn.Receive()
if err != nil {
select {
case <-m.stop:
return
default:
}
// Keep retrying while we're not closed.
m.logf("Error receiving from connection: %v", err)
time.Sleep(time.Second)
continue
}
select {
case m.change <- struct{}{}:
default:
}
}
}
// debounce calls the callback function with a delay between events
// and exits when a stop is issued.
func (m *Mon) debounce() {
for {
select {
case <-m.stop:
return
case <-m.change:
}
m.cb()
select {
case <-m.stop:
return
case <-time.After(100 * time.Millisecond):
}
}
}