ipn/ipnlocal: remove all the weird locking (LockedOnEntry, UnlockEarly, etc)

Fixes #11649
Updates #16369

Co-authored-by: James Sanderson <jsanderson@tailscale.com>
Change-Id: I63eaa18fe870ddf81d84b949efac4d1b44c3db86
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-11-08 17:23:33 -08:00
committed by Brad Fitzpatrick
parent 08e74effc0
commit 146ea42822
9 changed files with 362 additions and 507 deletions

View File

@@ -12,6 +12,8 @@ import (
type ExecQueue struct {
mu sync.Mutex
ctx context.Context // context.Background + closed on Shutdown
cancel context.CancelFunc // closes ctx
closed bool
inFlight bool // whether a goroutine is running q.run
doneWaiter chan struct{} // non-nil if waiter is waiting, then closed
@@ -24,6 +26,7 @@ func (q *ExecQueue) Add(f func()) {
if q.closed {
return
}
q.initCtxLocked()
if q.inFlight {
q.queue = append(q.queue, f)
} else {
@@ -79,18 +82,32 @@ func (q *ExecQueue) Shutdown() {
q.mu.Lock()
defer q.mu.Unlock()
q.closed = true
if q.cancel != nil {
q.cancel()
}
}
// Wait waits for the queue to be empty.
func (q *ExecQueue) initCtxLocked() {
if q.ctx == nil {
q.ctx, q.cancel = context.WithCancel(context.Background())
}
}
// Wait waits for the queue to be empty or shut down.
func (q *ExecQueue) Wait(ctx context.Context) error {
q.mu.Lock()
q.initCtxLocked()
waitCh := q.doneWaiter
if q.inFlight && waitCh == nil {
waitCh = make(chan struct{})
q.doneWaiter = waitCh
}
closed := q.closed
q.mu.Unlock()
if closed {
return errors.New("execqueue shut down")
}
if waitCh == nil {
return nil
}
@@ -98,6 +115,8 @@ func (q *ExecQueue) Wait(ctx context.Context) error {
select {
case <-waitCh:
return nil
case <-q.ctx.Done():
return errors.New("execqueue shut down")
case <-ctx.Done():
return ctx.Err()
}