From 4f7ad7d99ad5f2bf4fbfce624dc07a4e1f610edf Mon Sep 17 00:00:00 2001 From: claude Date: Thu, 6 Aug 2026 01:29:24 +0400 Subject: [PATCH] main.go: worker starts go through goWorker (V-575) Seven copies of add one, go, defer done. The helper keeps the WaitGroup registration next to the goroutine it counts, so a worker cannot be started without shutdown waiting for it. The UnlockFn goroutines keep the shape they had, wg and all. --- cmd/mavend/main.go | 64 +++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/cmd/mavend/main.go b/cmd/mavend/main.go index 9822dc0..8408d76 100644 --- a/cmd/mavend/main.go +++ b/cmd/mavend/main.go @@ -578,71 +578,39 @@ func run(args []string) error { } } - wg.Add(1) - go func() { - defer wg.Done() + goWorker(&wg, func() { if err := srv.Serve(); err != nil && !errors.Is(err, net.ErrClosed) { log.Printf("ipc serve: %v", err) } - }() + }) log.Printf("mavend: ipc listening on %s", srv.Path()) if !locked && voiceW != nil { - wg.Add(1) - go func() { - defer wg.Done() + goWorker(&wg, func() { if err := voiceW.server.Serve(); err != nil && !errors.Is(err, net.ErrClosed) { log.Printf("voice serve: %v", err) } - }() + }) log.Printf("mavend: voice listening on %s", voiceW.server.Addr()) } if !locked { - wg.Add(1) - go func() { - defer wg.Done() - tl.run(ctx) - }() - wg.Add(1) - go func() { - defer wg.Done() - factWorker.run(ctx) - }() + goWorker(&wg, func() { tl.run(ctx) }) + goWorker(&wg, func() { factWorker.run(ctx) }) if evalWorker != nil { - wg.Add(1) - go func() { - defer wg.Done() - evalWorker.run(ctx) - }() + goWorker(&wg, func() { evalWorker.run(ctx) }) } if feedWkr != nil { - wg.Add(1) - go func() { - defer wg.Done() - feedWkr.run(ctx) - }() + goWorker(&wg, func() { feedWkr.run(ctx) }) } if crawlWkr != nil { - wg.Add(1) - go func() { - defer wg.Done() - crawlWkr.run(ctx) - }() + goWorker(&wg, func() { crawlWkr.run(ctx) }) } if voiceW != nil && voiceW.mcp != nil { - wg.Add(1) - go func() { - defer wg.Done() - voiceW.mcp.run(ctx) - }() + goWorker(&wg, func() { voiceW.mcp.run(ctx) }) } if voiceW != nil && voiceW.home != nil { - wg.Add(1) - go func() { - defer wg.Done() - voiceW.home.run(ctx) - }() + goWorker(&wg, func() { voiceW.home.run(ctx) }) } } @@ -839,6 +807,16 @@ func wireTickLoop(st *store.Store, gatherer *loop.Gatherer, dispatcher *delivery cfg.PatternProposals) } +// goWorker starts run on its own goroutine and registers it with wg, so +// shutdown can wait for it inside workerGrace. +func goWorker(wg *sync.WaitGroup, run func()) { + wg.Add(1) + go func() { + defer wg.Done() + run() + }() +} + // wireRules builds the nudge rule set, minus anything config turned off. The // drop is logged because a rule vanishing silently is indistinguishable from a // rule that is broken, and the next person to wonder why she stopped nudging