wgengine/magicsock: wait for derphttp client goroutine to exit
@@ -1887,6 +1887,17 @@ func (c *Conn) Close() error {
c.pconn6.Close()
}
err := c.pconn4.Close()
+ // The goroutine running dc.Connect in derpWriteChanOfAddr may linger
+ // and appear to leak, as observed in https://github.com/tailscale/tailscale/issues/554.
+ // This is despite the underlying context being cancelled by connCtxCancel above.
+ // To avoid this condition, we must wait on derpStarted here
+ // to ensure that this goroutine has exited by the time Close returns.
+ // We only do this if derpWriteChanOfAddr has executed at least once:
+ // on the first run, it sets firstDerp := true and spawns the aforementioned goroutine.
+ // To detect this, we check activeDerp, which is initialized to non-nil on the first run.
+ if c.activeDerp != nil {
+ <-c.derpStarted
+ }
// Wait on endpoints updating right at the end, once everything is
// already closed. We want everything else in the Conn to be
// consistently in the closed state before we release mu to wait
