Skip to content

Commit ff8540d

Browse files
committed
Remove the circular reference
1 parent 73d316e commit ff8540d

5 files changed

Lines changed: 25 additions & 34 deletions

File tree

control.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type Control struct {
5353
statsStart func()
5454
dnsStart func()
5555
lighthouseStart func()
56-
networkChangeStart func()
56+
networkChangeStart func(rebind func())
5757
connectionManagerStart func(context.Context)
5858
}
5959

@@ -106,7 +106,7 @@ func (c *Control) Start() error {
106106
go c.dnsStart()
107107
}
108108
if c.networkChangeStart != nil {
109-
go c.networkChangeStart()
109+
go c.networkChangeStart(c.RebindUDPServer)
110110
}
111111
if c.connectionManagerStart != nil {
112112
go c.connectionManagerStart(c.ctx)

main.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,9 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev
268268

269269
attachCommands(l, c, ssh, ifce)
270270

271-
control := &Control{
271+
networkChanges := udp.NewNetworkChangeMonitor(ctx, l, c)
272+
273+
return &Control{
272274
state: StateReady,
273275
f: ifce,
274276
l: l,
@@ -278,15 +280,9 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev
278280
statsStart: stats.Start,
279281
dnsStart: ds.Start,
280282
lighthouseStart: lightHouse.StartUpdateWorker,
283+
networkChangeStart: networkChanges.Start,
281284
connectionManagerStart: connManager.Start,
282-
}
283-
284-
// The monitor's whole job is to trigger a rebind, and Control is what owns rebinding and the state gating
285-
// around it, so it is injected the rebind func and can only be built once Control exists.
286-
networkChanges := udp.NewNetworkChangeMonitor(ctx, l, c, control.RebindUDPServer)
287-
control.networkChangeStart = networkChanges.Start
288-
289-
return control, nil
285+
}, nil
290286
}
291287

292288
func moduleVersion() string {

udp/netchange.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,30 @@ import (
1111
//
1212
// Detection lives here in the udp package, next to the socket it concerns and the platform matrix that already knows
1313
// which sockets go stale. What to do about a change — updating the lighthouse, requerying tunnels — is not the udp
14-
// package's business, so the reaction is injected as a plain function pointer. The monitor calls it once a change
15-
// has settled and stays ignorant of what it does.
14+
// package's business, so Start takes the reaction as a plain function. Passing it at Start rather than holding it
15+
// keeps this package from referencing whatever owns the rebind.
1616
//
1717
// On platforms whose sockets do not go stale, watchNetworkChanges hands back a nil channel and Start returns.
1818
type NetworkChangeMonitor struct {
1919
l *slog.Logger
2020
ctx context.Context
2121
enabled bool
22-
// rebind is what we call once a change has settled. It is injected because the udp package has no business
23-
// knowing what a rebind entails, only when one is warranted.
24-
rebind func()
2522
}
2623

27-
// NewNetworkChangeMonitor builds a monitor that calls rebind whenever the local network moves. The returned monitor
28-
// is always usable: Start is safe to call unconditionally, it no-ops when disabled or on a platform that does not
29-
// need it.
30-
func NewNetworkChangeMonitor(ctx context.Context, l *slog.Logger, c *config.C, rebind func()) *NetworkChangeMonitor {
24+
// NewNetworkChangeMonitor builds a monitor for local network changes. The returned monitor is always usable: Start
25+
// is safe to call unconditionally, it no-ops when disabled or on a platform that does not need it.
26+
func NewNetworkChangeMonitor(ctx context.Context, l *slog.Logger, c *config.C) *NetworkChangeMonitor {
3127
return &NetworkChangeMonitor{
3228
l: l,
3329
ctx: ctx,
3430
enabled: c.GetBool("listen.rebind_on_network_change", true),
35-
rebind: rebind,
3631
}
3732
}
3833

3934
// Start watches for network changes until the context is cancelled, calling rebind once per settled change. It
4035
// blocks, so callers run it in a goroutine, and it no-ops when disabled, unsupported, or with nothing to rebind.
41-
func (m *NetworkChangeMonitor) Start() {
42-
if !m.enabled || m.rebind == nil || m.ctx.Err() != nil {
36+
func (m *NetworkChangeMonitor) Start(rebind func()) {
37+
if !m.enabled || rebind == nil || m.ctx.Err() != nil {
4338
return
4439
}
4540

@@ -61,6 +56,6 @@ func (m *NetworkChangeMonitor) Start() {
6156

6257
for range changes {
6358
m.l.Info("Local network changed, rebinding the udp listener")
64-
m.rebind()
59+
rebind()
6560
}
6661
}

udp/netchange_darwin_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,11 @@ func TestNetworkChangeMonitorStopsWithContext(t *testing.T) {
216216
l := test.NewLogger()
217217
c := config.NewC(l)
218218
require.NoError(t, c.LoadString("listen:\n rebind_on_network_change: true\n"))
219-
m := NewNetworkChangeMonitor(ctx, l, c, func() {})
219+
m := NewNetworkChangeMonitor(ctx, l, c)
220220

221221
done := make(chan struct{})
222222
go func() {
223-
m.Start()
223+
m.Start(func() {})
224224
close(done)
225225
}()
226226

@@ -240,5 +240,5 @@ func TestNetworkChangeMonitorStopsWithContext(t *testing.T) {
240240
}
241241

242242
// Starting again after the context is dead must not open anything.
243-
m.Start()
243+
m.Start(func() {})
244244
}

udp/netchange_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,30 @@ import (
1010
"github.com/stretchr/testify/require"
1111
)
1212

13-
func newMonitor(t *testing.T, ctx context.Context, cfg string, rebind func()) *NetworkChangeMonitor {
13+
func newMonitor(t *testing.T, ctx context.Context, cfg string) *NetworkChangeMonitor {
1414
t.Helper()
1515
l := test.NewLogger()
1616
c := config.NewC(l)
1717
require.NoError(t, c.LoadString(cfg))
18-
return NewNetworkChangeMonitor(ctx, l, c, rebind)
18+
return NewNetworkChangeMonitor(ctx, l, c)
1919
}
2020

2121
func TestNetworkChangeMonitorDefaultsOn(t *testing.T) {
2222
// Says nothing about rebinding, so this covers the default.
23-
m := newMonitor(t, context.Background(), "listen:\n host: 0.0.0.0\n", func() {})
23+
m := newMonitor(t, context.Background(), "listen:\n host: 0.0.0.0\n")
2424
assert.True(t, m.enabled, "should default to on")
2525
}
2626

2727
func TestNetworkChangeMonitorDisabledIsANoOp(t *testing.T) {
28-
m := newMonitor(t, context.Background(), "listen:\n rebind_on_network_change: false\n", func() {})
28+
m := newMonitor(t, context.Background(), "listen:\n rebind_on_network_change: false\n")
2929
require.False(t, m.enabled)
3030

3131
// Must return without opening a socket. If it watched anything this would block.
32-
m.Start()
32+
m.Start(func() {})
3333
}
3434

3535
func TestNetworkChangeMonitorNilRebindIsANoOp(t *testing.T) {
3636
// Nothing to rebind, so there is no point watching, on any platform.
37-
m := newMonitor(t, context.Background(), "listen:\n rebind_on_network_change: true\n", nil)
38-
m.Start()
37+
m := newMonitor(t, context.Background(), "listen:\n rebind_on_network_change: true\n")
38+
m.Start(nil)
3939
}

0 commit comments

Comments
 (0)