From a5f3f2898b3b5a020961de5a9a1e8e164db42c64 Mon Sep 17 00:00:00 2001 From: Catherine Date: Mon, 1 Jun 2026 13:49:45 +0000 Subject: [PATCH] [breaking-change] Remove SIGHUP handler entirely. The implementation contains both data races on single-word memory accesses as well as multiple-word memory accesses which can result in observing torn writes. It is unsafe and unsalvageable without wrapping every access to global state into a mutex, or else stopping request processing during a reload. Both are invasive options. Since the server restarts very quickly, remove the handler to fix this. --- src/main.go | 33 --------------------------------- src/sys/signal.go | 14 +------------- 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/src/main.go b/src/main.go index 61380bc..e1d63d1 100644 --- a/src/main.go +++ b/src/main.go @@ -788,39 +788,6 @@ func Main(versionInfo string) { } default: - // Hook a signal (SIGHUP on *nix, nothing on Windows) for reloading the configuration - // at runtime. This is useful because it preserves S3 backend cache contents. Failed - // configuration reloads will not crash the process; you may want to check the syntax - // first with `git-pages -config ... -print-config` since there is no other feedback. - // - // Note that not all of the configuration is updated on reload. Listeners are kept as-is. - // The backend is not recreated (this is intentional as it allows preserving the cache). - sys.OnReload(func() { - if newConfig, err := Configure(*configTomlPath, *secretTomlPath); err != nil { - logc.Println(ctx, "config: reload err:", err) - } else { - // From https://go.dev/ref/mem: - // > A read r of a memory location x holding a value that is not larger than - // > a machine word must observe some write w such that r does not happen before - // > w and there is no write w' such that w happens before w' and w' happens - // > before r. That is, each read must observe a value written by a preceding or - // > concurrent write. - config = newConfig - if err = errors.Join( - configureFeatures(ctx), - configureMemLimit(ctx), - configureWildcards(ctx), - configureFallback(ctx), - ); err != nil { - // At this point the configuration is in an in-between, corrupted state, so - // the only reasonable choice is to crash. - logc.Fatalln(ctx, "config: reload fail:", err) - } else { - logc.Println(ctx, "config: reload ok") - } - } - }) - // Start listening on all ports before initializing the backend, otherwise if the backend // spends some time initializing (which the S3 backend does) a proxy like Caddy can race // with git-pages on startup and return errors for requests that would have been served diff --git a/src/sys/signal.go b/src/sys/signal.go index a53429d..e4bd682 100644 --- a/src/sys/signal.go +++ b/src/sys/signal.go @@ -1,6 +1,5 @@ // See https://pkg.go.dev/os/signal#hdr-Windows for a description of what this module -// will do on Windows (tl;dr nothing calls the reload handler, the interrupt handler works -// more or less how you'd expect). +// will do on Windows. package sys @@ -10,17 +9,6 @@ import ( "syscall" ) -func OnReload(handler func()) { - sighup := make(chan os.Signal, 1) - signal.Notify(sighup, syscall.SIGHUP) - go func() { - for { - <-sighup - handler() - } - }() -} - func WaitForInterrupt() { sigint := make(chan os.Signal, 1) signal.Notify(sigint, syscall.SIGINT, syscall.SIGTERM)