@@ -619,9 +619,13 @@ func (app *App) Start(ctx context.Context) (err error) {
619619 })
620620}
621621
622- func (app * App ) start (ctx context.Context ) error {
623- if err := app .lifecycle .Start (ctx ); err != nil {
624- // Start failed, rolling back.
622+ // withRollback will execute an anonymous function with a given context.
623+ // if the anon func returns an error, rollback methods will be called and related events emitted
624+ func (app * App ) withRollback (
625+ ctx context.Context ,
626+ f func (context.Context ) error ,
627+ ) error {
628+ if err := f (ctx ); err != nil {
625629 app .log ().LogEvent (& fxevent.RollingBack {StartErr : err })
626630
627631 stopErr := app .lifecycle .Stop (ctx )
@@ -633,9 +637,20 @@ func (app *App) start(ctx context.Context) error {
633637
634638 return err
635639 }
640+
636641 return nil
637642}
638643
644+ func (app * App ) start (ctx context.Context ) error {
645+ return app .withRollback (ctx , func (ctx context.Context ) error {
646+ if err := app .lifecycle .Start (ctx ); err != nil {
647+ return err
648+ }
649+ app .receivers .Start (ctx )
650+ return nil
651+ })
652+ }
653+
639654// Stop gracefully stops the application. It executes any registered OnStop
640655// hooks in reverse order, so that each constructor's stop hooks are called
641656// before its dependencies' stop hooks.
@@ -648,9 +663,14 @@ func (app *App) Stop(ctx context.Context) (err error) {
648663 app .log ().LogEvent (& fxevent.Stopped {Err : err })
649664 }()
650665
666+ cb := func (ctx context.Context ) error {
667+ defer app .receivers .Stop (ctx )
668+ return app .lifecycle .Stop (ctx )
669+ }
670+
651671 return withTimeout (ctx , & withTimeoutParams {
652672 hook : _onStopHook ,
653- callback : app . lifecycle . Stop ,
673+ callback : cb ,
654674 lifecycle : app .lifecycle ,
655675 log : app .log (),
656676 })
@@ -663,10 +683,25 @@ func (app *App) Stop(ctx context.Context) (err error) {
663683//
664684// Alternatively, a signal can be broadcast to all done channels manually by
665685// using the Shutdown functionality (see the Shutdowner documentation for details).
686+ //
687+ // Note: The channel Done returns will not receive a signal unless the application
688+ // as been started via Start or Run.
666689func (app * App ) Done () <- chan os.Signal {
667690 return app .receivers .Done ()
668691}
669692
693+ // Wait returns a channel of [ShutdownSignal] to block on after starting the
694+ // application and function, similar to [App.Done], but with a minor difference.
695+ // Should an ExitCode be provided as a [ShutdownOption] to
696+ // the Shutdowner Shutdown method, the exit code will be available as part
697+ // of the ShutdownSignal struct.
698+ //
699+ // Should the app receive a SIGTERM or SIGINT, the given
700+ // signal will be populated in the ShutdownSignal struct.
701+ func (app * App ) Wait () <- chan ShutdownSignal {
702+ return app .receivers .Wait ()
703+ }
704+
670705// StartTimeout returns the configured startup timeout. Apps default to using
671706// DefaultTimeout, but users can configure this behavior using the
672707// StartTimeout option.
0 commit comments