@@ -55,22 +55,28 @@ func (u *nodeUpdater) updateLoop() {
55
55
u .logger .Debug (context .Background (), "closing nodeUpdater updateLoop" )
56
56
return
57
57
}
58
- node := u .nodeLocked ()
59
58
u .dirty = false
60
59
u .phase = configuring
61
60
u .Broadcast ()
62
61
62
+ callback := u .callback
63
+ if callback == nil {
64
+ u .logger .Debug (context .Background (), "skipped sending node; no node callback" )
65
+ continue
66
+ }
67
+
63
68
// We cannot reach nodes without DERP for discovery. Therefore, there is no point in sending
64
69
// the node without this, and we can save ourselves from churn in the tailscale/wireguard
65
70
// layer.
71
+ node := u .nodeLocked ()
66
72
if node .PreferredDERP == 0 {
67
73
u .logger .Debug (context .Background (), "skipped sending node; no PreferredDERP" , slog .F ("node" , node ))
68
74
continue
69
75
}
70
76
71
77
u .L .Unlock ()
72
78
u .logger .Debug (context .Background (), "calling nodeUpdater callback" , slog .F ("node" , node ))
73
- u . callback (node )
79
+ callback (node )
74
80
u .L .Lock ()
75
81
}
76
82
}
@@ -155,7 +161,7 @@ func (u *nodeUpdater) setDERPForcedWebsocket(region int, reason string) {
155
161
}
156
162
157
163
// setStatus handles the status callback from the wireguard engine to learn about new endpoints
158
- // (e.g. discovered by STUN)
164
+ // (e.g. discovered by STUN). u.L MUST NOT be held
159
165
func (u * nodeUpdater ) setStatus (s * wgengine.Status , err error ) {
160
166
u .logger .Debug (context .Background (), "wireguard status" , slog .F ("status" , s ), slog .Error (err ))
161
167
if err != nil {
@@ -181,6 +187,7 @@ func (u *nodeUpdater) setStatus(s *wgengine.Status, err error) {
181
187
u .Broadcast ()
182
188
}
183
189
190
+ // setAddresses sets the local addresses for the node. u.L MUST NOT be held.
184
191
func (u * nodeUpdater ) setAddresses (ips []netip.Prefix ) {
185
192
u .L .Lock ()
186
193
defer u .L .Unlock ()
@@ -192,3 +199,13 @@ func (u *nodeUpdater) setAddresses(ips []netip.Prefix) {
192
199
u .dirty = true
193
200
u .Broadcast ()
194
201
}
202
+
203
+ // setCallback sets the callback for node changes. It also triggers a call
204
+ // for the current node immediately. u.L MUST NOT be held.
205
+ func (u * nodeUpdater ) setCallback (callback func (node * Node )) {
206
+ u .L .Lock ()
207
+ defer u .L .Unlock ()
208
+ u .callback = callback
209
+ u .dirty = true
210
+ u .Broadcast ()
211
+ }
0 commit comments