From 629adb056befef2bab9d20c30772d15de1d3264a Mon Sep 17 00:00:00 2001 From: Snider Date: Fri, 20 Mar 2026 17:56:21 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20lifecycle=20=E2=80=94=20clear=20shutdown?= =?UTF-8?q?=20flag=20on=20startup,=20document=20waiter=20goroutine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ServiceStartup clears c.shutdown so Core supports restart cycles - ServiceShutdown waiter goroutine documented as inherent to sync.WaitGroup Co-Authored-By: Virgil --- pkg/core/runtime.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/core/runtime.go b/pkg/core/runtime.go index fa2d054..a81afbd 100644 --- a/pkg/core/runtime.go +++ b/pkg/core/runtime.go @@ -33,6 +33,7 @@ func (r *ServiceRuntime[T]) Config() *Config { return r.core.Config() } // ServiceStartup runs OnStart for all registered services that have one. func (c *Core) ServiceStartup(ctx context.Context, options any) Result { + c.shutdown.Store(false) startables := c.Startables() if startables.OK { for _, s := range startables.Value.([]*Service) { @@ -54,7 +55,9 @@ func (c *Core) ServiceShutdown(ctx context.Context) Result { c.shutdown.Store(true) c.ACTION(ActionServiceShutdown{}) - // Drain background tasks before stopping services + // Drain background tasks before stopping services. + // On timeout, the waiter goroutine persists until tasks complete — + // this is inherent to sync.WaitGroup (no cancel mechanism). done := make(chan struct{}) go func() { c.wg.Wait()