updateLoop waits until the config is dirty and then calls the callback with the newest node. It is intended only to be called internally, and shuts down when close() is called.
()
| 42 | // updateLoop waits until the config is dirty and then calls the callback with the newest node. |
| 43 | // It is intended only to be called internally, and shuts down when close() is called. |
| 44 | func (u *nodeUpdater) updateLoop() { |
| 45 | u.L.Lock() |
| 46 | defer u.L.Unlock() |
| 47 | defer func() { |
| 48 | u.phase = closed |
| 49 | u.Broadcast() |
| 50 | }() |
| 51 | for { |
| 52 | for !(u.closing || u.dirty) { |
| 53 | u.phase = idle |
| 54 | u.Wait() |
| 55 | } |
| 56 | if u.closing { |
| 57 | u.logger.Debug(context.Background(), "closing nodeUpdater updateLoop") |
| 58 | return |
| 59 | } |
| 60 | u.dirty = false |
| 61 | u.phase = configuring |
| 62 | u.Broadcast() |
| 63 | |
| 64 | callback := u.callback |
| 65 | if callback == nil { |
| 66 | u.logger.Debug(context.Background(), "skipped sending node; no node callback") |
| 67 | continue |
| 68 | } |
| 69 | |
| 70 | // We cannot reach nodes without DERP for discovery. Therefore, there is no point in sending |
| 71 | // the node without this, and we can save ourselves from churn in the tailscale/wireguard |
| 72 | // layer. |
| 73 | node := u.nodeLocked() |
| 74 | if node.PreferredDERP == 0 { |
| 75 | u.logger.Debug(context.Background(), "skipped sending node; no PreferredDERP", slog.F("node", node)) |
| 76 | continue |
| 77 | } |
| 78 | |
| 79 | u.L.Unlock() |
| 80 | u.logger.Debug(context.Background(), "calling nodeUpdater callback", slog.F("node", node)) |
| 81 | callback(node) |
| 82 | u.L.Lock() |
| 83 | u.sentNode = true |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | // close closes the nodeUpdate and stops it calling the node callback |
| 88 | func (u *nodeUpdater) close() { |
no test coverage detected