From 6e7e2aefa5acb3228080daf6a7aa9be4261cc87d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 5 Oct 2026 22:59:05 +0200 Subject: [PATCH] daemon/command: setupConfigReloadTrap: handle context cancellation Pass the daemon lifecycle context to the config reload trap and stop the Unix signal handler when the context is canceled. This also unregisters the SIGHUP notification when the goroutine exits. On Windows, signal the Win32 event when the context is canceled to wake the blocking wait and allow the reload goroutine to exit. Signed-off-by: Sebastiaan van Stijn --- daemon/command/daemon.go | 2 +- daemon/command/daemon_unix.go | 17 ++++++++++++++--- daemon/command/daemon_windows.go | 20 ++++++++++++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/daemon/command/daemon.go b/daemon/command/daemon.go index 2b63ea32d4..85c7709ad1 100644 --- a/daemon/command/daemon.go +++ b/daemon/command/daemon.go @@ -363,7 +363,7 @@ func (cli *daemonCLI) start(ctx context.Context) (retErr error) { go d.ProcessClusterNotifications(ctx, c.GetWatchStream()) - cli.setupConfigReloadTrap() + cli.setupConfigReloadTrap(ctx) log.G(ctx).Info("Daemon has completed initialization") diff --git a/daemon/command/daemon_unix.go b/daemon/command/daemon_unix.go index e2345074ef..9d369162df 100644 --- a/daemon/command/daemon_unix.go +++ b/daemon/command/daemon_unix.go @@ -58,12 +58,23 @@ func setDefaultUmask() error { } // setupConfigReloadTrap configures the SIGHUP signal to reload the configuration. -func (cli *daemonCLI) setupConfigReloadTrap() { +func (cli *daemonCLI) setupConfigReloadTrap(ctx context.Context) { c := make(chan os.Signal, 1) signal.Notify(c, unix.SIGHUP) + go func() { - for range c { - cli.reloadConfig() + defer signal.Stop(c) + + for { + select { + case <-ctx.Done(): + return + case <-c: + if ctx.Err() != nil { + return + } + cli.reloadConfig() + } } }() } diff --git a/daemon/command/daemon_windows.go b/daemon/command/daemon_windows.go index e5bc8e6850..3a4dd9d95f 100644 --- a/daemon/command/daemon_windows.go +++ b/daemon/command/daemon_windows.go @@ -80,9 +80,7 @@ func notifyShutdown(ctx context.Context, err error) { } // setupConfigReloadTrap configures a Win32 event to reload the configuration. -func (cli *daemonCLI) setupConfigReloadTrap() { - ctx := context.TODO() - +func (cli *daemonCLI) setupConfigReloadTrap(ctx context.Context) { go func() { event := `Global\docker-daemon-config-` + strconv.Itoa(os.Getpid()) ev, _ := windows.UTF16PtrFromString(event) @@ -91,11 +89,25 @@ func (cli *daemonCLI) setupConfigReloadTrap() { log.G(ctx).WithError(err).Errorf("Failed to create config reload event %s", event) return } - defer func() { _ = windows.CloseHandle(h) }() + stopDone := make(chan struct{}) + stop := context.AfterFunc(ctx, func() { + // Wake the blocking wait so the goroutine can exit on shutdown. + _ = windows.SetEvent(h) + close(stopDone) + }) + defer func() { + if !stop() { + <-stopDone + } + _ = windows.CloseHandle(h) + }() log.G(ctx).Debugf("Config reload - waiting signal at %s", event) for { windows.WaitForSingleObject(h, windows.INFINITE) + if ctx.Err() != nil { + return + } cli.reloadConfig() } }()