aboutsummaryrefslogtreecommitdiff
path: root/cmd/mobius-hotline-server
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2026-06-12 08:26:25 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2026-06-12 08:26:25 -0700
commitb2c462a3a1353f0653a5964b3a6924538ce83523 (patch)
tree90968ec234bbfa44e58fcf887efd9ba15dc280dd /cmd/mobius-hotline-server
parent7ebc802d0a269218f05b3b51eb10ac66eacb4d1f (diff)
Shut down gracefully instead of exiting from library code
ListenAndServe previously started each listener in a goroutine that called log.Fatal on any error, which skipped deferred cleanup and made errors unobservable to callers, and Server.Shutdown terminated the process with os.Exit. Context cancellation was also ineffective: Serve only checked ctx between Accept calls, which block indefinitely. ListenAndServe now binds its listeners up front and returns bind errors, closes every listener when the context is canceled so accept loops unblock and return, and reports the first serve loop error to the caller. Shutdown closes a lazily-initialized channel that cancels ListenAndServe's context, so the shutdown API works race-free even though it starts before ListenAndServe. "Server shutting down" is logged once by ListenAndServe rather than per accept loop, which produced duplicate or missing lines depending on scheduling. main.go now treats context.Canceled as a clean exit, logs other server errors and exits nonzero, and runs deferred cleanup (e.g. Bonjour shutdown) on the way out.
Diffstat (limited to 'cmd/mobius-hotline-server')
-rw-r--r--cmd/mobius-hotline-server/main.go14
1 files changed, 10 insertions, 4 deletions
diff --git a/cmd/mobius-hotline-server/main.go b/cmd/mobius-hotline-server/main.go
index dced2c5..1c298a2 100644
--- a/cmd/mobius-hotline-server/main.go
+++ b/cmd/mobius-hotline-server/main.go
@@ -4,10 +4,10 @@ import (
"context"
"crypto/tls"
"embed"
+ "errors"
"flag"
"fmt"
"io"
- "log"
"os"
"os/signal"
"path"
@@ -207,9 +207,9 @@ func main() {
reloadFunc()
default:
+ // Canceling the context stops ListenAndServe, which unblocks main for a clean exit.
signal.Stop(sigChan)
cancel()
- os.Exit(0)
}
}
@@ -235,8 +235,14 @@ func main() {
defer s.Shutdown()
}
- // Serve Hotline requests until program exit
- log.Fatal(srv.ListenAndServe(ctx))
+ // Serve Hotline requests until shutdown is requested via signal, the shutdown API, or a
+ // server error.
+ if err := srv.ListenAndServe(ctx); err != nil && !errors.Is(err, context.Canceled) {
+ slogger.Error("Server error", "err", err)
+ os.Exit(1)
+ }
+
+ slogger.Info("Server shut down")
}
// findConfigPath searches for an existing config directory from the predefined search order.