Live video on the AT Protocol
at eli/godeps 54 lines 1.1 kB view raw
1package cmd 2 3import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "golang.org/x/sync/errgroup" 10 "stream.place/streamplace/pkg/log" 11) 12 13func TimeoutGroupWithContext(ctx context.Context) (*TimeoutGroup, context.Context) { 14 group, ctx2 := errgroup.WithContext(ctx) 15 tg := &TimeoutGroup{ 16 ErrGroup: group, 17 exitCh: make(chan error), 18 } 19 return tg, ctx2 20} 21 22// errgroup wrapper that self-destructs if things aren't shutting down properly 23type TimeoutGroup struct { 24 ErrGroup *errgroup.Group 25 selfDestructMu sync.Mutex 26 exitCh chan error 27} 28 29func (g *TimeoutGroup) Go(f func() error) { 30 g.ErrGroup.Go(func() error { 31 err := f() 32 g.selfDestruct(err) 33 return err 34 }) 35} 36 37func (g *TimeoutGroup) Wait() error { 38 go func() { 39 g.exitCh <- g.ErrGroup.Wait() 40 }() 41 return <-g.exitCh 42} 43 44func (g *TimeoutGroup) selfDestruct(err error) { 45 first := g.selfDestructMu.TryLock() 46 if !first { 47 return 48 } 49 go func() { 50 log.Log(context.Background(), "app terminating", "reason", err) 51 time.Sleep(5 * time.Second) 52 g.exitCh <- fmt.Errorf("selfDestruct terminated app after timeout, reason for shutdown: %w", err) 53 }() 54}