Skip to content

Commit

Permalink
Support Shutdown from fx.Invoke after App.Start
Browse files Browse the repository at this point in the history
App.Start nils out the "last" signal recorded by signalReceivers,
which it otherwise broadcasts to waiters if it was already received.
This is unnecessary especially because there's a discrepancy in behavior
of using App.Start vs App.Run when shutting down from fx.Invoke.

Given a program that calls Shutdown from fx.Invoke,
when we do:

    app := fx.New(...)

The shutdowner has already sent the signal, and signalReceivers has
already recorded it.
At that point, whether we call App.Start or App.Run changes behavior:

- If we call App.Run, that calls App.Done (or App.Wait after uber-go#1075),
  which gives it back a channel that already has the signal filled in.
  It then calls App.Start, waits on the channel--which returns
  immediately--and then calls App.Stop.
- If we call App.Start and App.Wait, on the other hand,
  Start will clear the signal recorded in signalReceivers,
  and then App.Wait will build a channel that will block indefinitely
  because Shutdowner.Shutdown will not be called again.

So even though App.Run() and App.Start()+App.Wait() are meant to be
equivalent, this causes a serious discrepancy in behavior.
It makes sense to resolve this by supporting Shutdown from Invoke.

Refs uber-go#1074
  • Loading branch information
abhinav committed Apr 29, 2023
1 parent accd698 commit 1f3bbe9
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 3 deletions.
2 changes: 0 additions & 2 deletions shutdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ type shutdowner struct {
// Shutdown broadcasts a signal to all of the application's Done channels
// and begins the Stop process. Applications can be shut down only after they
// have finished starting up.
// In practice this means Shutdowner.Shutdown should not be called from an
// fx.Invoke, but from a fx.Lifecycle.OnStart hook.
func (s *shutdowner) Shutdown(opts ...ShutdownOption) error {
for _, opt := range opts {
opt.apply(s)
Expand Down
1 change: 0 additions & 1 deletion signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ func (recv *signalReceivers) Start(ctx context.Context) {
return
}

recv.last = nil
recv.finished = make(chan struct{}, 1)
recv.shutdown = make(chan struct{}, 1)
recv.notify(recv.signals, os.Interrupt, _sigINT, _sigTERM)
Expand Down

0 comments on commit 1f3bbe9

Please sign in to comment.