@@ -275,7 +275,9 @@ type App struct {
275275 err error
276276 clock fxclock.Clock
277277 lifecycle * lifecycleWrapper
278- stopch chan struct {} // closed when Stop is called
278+
279+ stopch chan struct {} // closed when Stop is called
280+ stopChLock sync.RWMutex // mutex for init and closing of stopch
279281
280282 container * dig.Container
281283 root * module
@@ -400,7 +402,6 @@ func New(opts ...Option) *App {
400402 clock : fxclock .System ,
401403 startTimeout : DefaultTimeout ,
402404 stopTimeout : DefaultTimeout ,
403- stopch : make (chan struct {}),
404405 }
405406
406407 app .root = & module {
@@ -563,6 +564,7 @@ func (app *App) run(done <-chan ShutdownSignal) (exitCode int) {
563564 defer cancel ()
564565
565566 if err := app .Start (startCtx ); err != nil {
567+ app .closeStopChannel ()
566568 return 1
567569 }
568570
@@ -628,6 +630,8 @@ func (app *App) Start(ctx context.Context) (err error) {
628630 return app .err
629631 }
630632
633+ app .initStopChannel ()
634+
631635 return withTimeout (ctx , & withTimeoutParams {
632636 hook : _onStartHook ,
633637 callback : app .start ,
@@ -653,6 +657,30 @@ func (app *App) start(ctx context.Context) error {
653657 return nil
654658}
655659
660+ func (app * App ) initStopChannel () {
661+ app .stopChLock .Lock ()
662+ defer app .stopChLock .Unlock ()
663+ if app .stopch == nil {
664+ app .stopch = make (chan struct {})
665+ }
666+ }
667+
668+ func (app * App ) stopChannel () chan struct {} {
669+ app .stopChLock .RLock ()
670+ defer app .stopChLock .RUnlock ()
671+ ch := app .stopch
672+ return ch
673+ }
674+
675+ func (app * App ) closeStopChannel () {
676+ app .stopChLock .Lock ()
677+ defer app .stopChLock .Unlock ()
678+ if app .stopch != nil {
679+ close (app .stopch )
680+ app .stopch = nil
681+ }
682+ }
683+
656684// Stop gracefully stops the application. It executes any registered OnStop
657685// hooks in reverse order, so that each constructor's stop hooks are called
658686// before its dependencies' stop hooks.
@@ -666,7 +694,7 @@ func (app *App) Stop(ctx context.Context) (err error) {
666694 // Protect the Stop hooks from being called multiple times.
667695 app .runStop .Do (func () {
668696 app .log ().LogEvent (& fxevent.Stopped {Err : err })
669- close ( app .stopch )
697+ app .closeStopChannel ( )
670698 })
671699 }()
672700
@@ -724,10 +752,14 @@ func (app *App) appendSignalReceiver(r signalReceiver) {
724752 sigch := make (chan os.Signal , 1 )
725753 signal .Notify (sigch , os .Interrupt , _sigINT , _sigTERM )
726754 go func () {
727- select {
728- case sig := <- sigch :
729- app .broadcastSignal (sig , 1 )
730- case <- app .stopch :
755+ // if the stop channel is nil; that means that the app was never started
756+ // thus, do not broadcast any signals
757+ if stopch := app .stopChannel (); stopch != nil {
758+ select {
759+ case sig := <- sigch :
760+ app .broadcastSignal (sig , 1 )
761+ case <- stopch :
762+ }
731763 }
732764 }()
733765 })
0 commit comments