wgengine/monitor: change API to permit multiple independent callbakcks

Currently it assumes exactly 1 registered callback. This changes it to
support 0, 1, or more than 1.

This is a step towards plumbing wgengine/monitor into more places (and
moving some of wgengine's interface state fetching into monitor in a
later step)

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/1409/head
Brad Fitzpatrick 3 years ago
parent 0eea490724
commit dda03a911e

@ -69,13 +69,14 @@ func runMonitor(ctx context.Context) error {
j, _ := json.MarshalIndent(st, "", " ")
os.Stderr.Write(j)
}
mon, err := monitor.New(log.Printf, func() {
log.Printf("Link monitor fired. State:")
dump()
})
mon, err := monitor.New(log.Printf)
if err != nil {
return err
}
mon.RegisterChangeCallback(func() {
log.Printf("Link monitor fired. State:")
dump()
})
log.Printf("Starting link change monitor; initial state:")
dump()
mon.Start()

@ -36,23 +36,28 @@ type osMon interface {
// an interface status changes.
type ChangeFunc func()
// An allocated callbackHandle's address is the Mon.cbs map key.
type callbackHandle byte
// Mon represents a monitoring instance.
type Mon struct {
logf logger.Logf
cb ChangeFunc
om osMon // nil means not supported on this platform
change chan struct{}
stop chan struct{}
mu sync.Mutex // guards cbs
cbs map[*callbackHandle]ChangeFunc
onceStart sync.Once
started bool
goroutines sync.WaitGroup
}
// New instantiates and starts a monitoring instance. Change notifications
// are propagated to the callback function.
// New instantiates and starts a monitoring instance.
// The returned monitor is inactive until it's started by the Start method.
func New(logf logger.Logf, callback ChangeFunc) (*Mon, error) {
// Use RegisterChangeCallback to get notified of network changes.
func New(logf logger.Logf) (*Mon, error) {
logf = logger.WithPrefix(logf, "monitor: ")
om, err := newOSMon(logf)
if err != nil {
@ -60,13 +65,28 @@ func New(logf logger.Logf, callback ChangeFunc) (*Mon, error) {
}
return &Mon{
logf: logf,
cb: callback,
cbs: map[*callbackHandle]ChangeFunc{},
om: om,
change: make(chan struct{}, 1),
stop: make(chan struct{}),
}, nil
}
// RegisterChangeCallback adds callback to the set of parties to be
// notified (in their own goroutine) when the network state changes.
// To remove this callback, call unregister (or close the monitor).
func (m *Mon) RegisterChangeCallback(callback ChangeFunc) (unregister func()) {
handle := new(callbackHandle)
m.mu.Lock()
defer m.mu.Unlock()
m.cbs[handle] = callback
return func() {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.cbs, handle)
}
}
// Start starts the monitor.
// A monitor can only be started & closed once.
func (m *Mon) Start() {
@ -136,7 +156,11 @@ func (m *Mon) debounce() {
case <-m.change:
}
m.cb()
m.mu.Lock()
for _, cb := range m.cbs {
go cb()
}
m.mu.Unlock()
select {
case <-m.stop:

@ -256,14 +256,16 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
e.linkState, _ = getLinkState()
logf("link state: %+v", e.linkState)
mon, err := monitor.New(logf, func() {
e.LinkChange(false)
tshttpproxy.InvalidateCache()
})
mon, err := monitor.New(logf)
if err != nil {
return nil, err
}
closePool.add(mon)
unregisterMonWatch := mon.RegisterChangeCallback(func() {
e.LinkChange(false)
tshttpproxy.InvalidateCache()
})
closePool.addFunc(unregisterMonWatch)
e.linkMon = mon
endpointsFn := func(endpoints []string) {

Loading…
Cancel
Save