// Package app provides small process lifecycle primitives for service app packages. package app import ( "context" "sync" ) // BackgroundGroup owns a cancellable worker context and waits for all registered workers to finish. type BackgroundGroup struct { ctx context.Context cancel context.CancelFunc wg sync.WaitGroup once sync.Once } // NewBackground creates a worker group rooted at parent; nil parent means context.Background. func NewBackground(parent context.Context) *BackgroundGroup { if parent == nil { parent = context.Background() } ctx, cancel := context.WithCancel(parent) return &BackgroundGroup{ctx: ctx, cancel: cancel} } // Context returns the shared worker context. Workers should stop when it is canceled. func (g *BackgroundGroup) Context() context.Context { if g == nil || g.ctx == nil { return context.Background() } return g.ctx } // Go starts one worker and gives it the shared cancellable context. func (g *BackgroundGroup) Go(run func(context.Context)) { if g == nil || run == nil { return } g.wg.Add(1) go func() { defer g.wg.Done() run(g.Context()) }() } // Stop cancels the shared worker context. It is safe to call more than once. func (g *BackgroundGroup) Stop() { if g == nil { return } g.once.Do(g.cancel) } // StopAndWait cancels the shared context and waits for all workers to return. func (g *BackgroundGroup) StopAndWait() { if g == nil { return } g.Stop() g.wg.Wait() }