From b2697ceb7d001d8fa514a0f58b7f9a787f03d4b6 Mon Sep 17 00:00:00 2001 From: Abhinav Gupta Date: Sat, 29 Apr 2023 11:24:06 -0700 Subject: [PATCH] Support Shutdown from fx.Invoke after App.Start 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 #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 #1074 --- shutdown.go | 2 -- signal.go | 1 - 2 files changed, 3 deletions(-) diff --git a/shutdown.go b/shutdown.go index aa81e68d3..d1e1b9553 100644 --- a/shutdown.go +++ b/shutdown.go @@ -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) diff --git a/signal.go b/signal.go index 1593c5de2..441ae8a92 100644 --- a/signal.go +++ b/signal.go @@ -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)