package lifecycle import ( "context" "sync" ) // HandlerGroup stops new handlers, cancels their shared lifecycle context, // and waits for handlers already using application dependencies. type HandlerGroup struct { ctx context.Context cancel context.CancelFunc mu sync.Mutex done *sync.Cond active int stopped bool } func NewHandlerGroup() *HandlerGroup { ctx, cancel := context.WithCancel(context.Background()) g := &HandlerGroup{ctx: ctx, cancel: cancel} g.done = sync.NewCond(&g.mu) return g } func (g *HandlerGroup) Context() context.Context { return g.ctx } func (g *HandlerGroup) Begin() bool { g.mu.Lock() defer g.mu.Unlock() if g.stopped { return false } g.active++ return true } func (g *HandlerGroup) Done() { g.mu.Lock() g.active-- if g.active == 0 { g.done.Broadcast() } g.mu.Unlock() } func (g *HandlerGroup) Stop() { g.mu.Lock() if !g.stopped { g.stopped = true g.cancel() } g.mu.Unlock() } func (g *HandlerGroup) Wait() { g.mu.Lock() for g.active != 0 { g.done.Wait() } g.mu.Unlock() }