signaler: improve cross-platform signal compatibility (#1952)

* signaler: improve cross-platform signal compatibility

- Add runtime-based platform detection for signal handling
- Include os.Kill only on Windows (cannot be caught/ignored on Unix)
- Refactor signal initialization into separate getPlatformSignals function

* signaler: add dependency injection and improve docs

- Introduce SignalNotifier interface for testability
- Add osSignalNotifier implementation and factory function
- Enhance package and function documentation
- Maintain backward compatibility with existing API

* [signaler] simplify shutdown handling; drop SIGABRT/os.Kill

* skip signaler tests on windows

* support signaler interrupt tests and improve signal handling tests

* removing getPlatformSignal function

* remove signaler readme

* Signaler: Return a channel from WaitForInterrupt

* Signaler: fix comment

Co-authored-by: Ryan O'Hara-Reid <oharareid.ryan@gmail.com>

* Signaler: require NoError to NoErrorf

Co-authored-by: Ryan O'Hara-Reid <oharareid.ryan@gmail.com>

---------

Co-authored-by: Ryan O'Hara-Reid <oharareid.ryan@gmail.com>
This commit is contained in:
Romano
2025-12-10 06:44:48 +01:00
committed by GitHub
parent 78382afb14
commit 1b83db3489
6 changed files with 48 additions and 21 deletions

View File

@@ -1,3 +1,4 @@
// Package signaler provides cross-platform signal handling for graceful application shutdown
package signaler
import (
@@ -6,20 +7,9 @@ import (
"syscall"
)
var s = make(chan os.Signal, 1)
func init() {
sigs := []os.Signal{
os.Interrupt,
os.Kill,
syscall.SIGTERM,
syscall.SIGABRT,
}
signal.Notify(s, sigs...)
}
// WaitForInterrupt waits until a os.Signal is
// received and returns the result
func WaitForInterrupt() os.Signal {
return <-s
// WaitForInterrupt returns a channel to receive termination signals
func WaitForInterrupt() chan os.Signal {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
return c
}

37
signaler/signaler_test.go Normal file
View File

@@ -0,0 +1,37 @@
package signaler
import (
"os"
"runtime"
"syscall"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWaitForInterrupt(t *testing.T) {
t.Parallel()
for _, sig := range []os.Signal{syscall.SIGTERM, os.Interrupt} {
sigC := WaitForInterrupt()
proc, err := os.FindProcess(os.Getpid())
require.NoError(t, err, "os.FindProcess must not error")
if err := proc.Signal(sig); err != nil {
if runtime.GOOS == "windows" {
t.Skipf("proc.Signal(%s) not supported on Windows: %v", sig, err)
}
require.NoErrorf(t, err, "proc.Signal(%s) must not error", sig)
}
assert.Eventuallyf(t, func() bool {
select {
case got := <-sigC:
return got == sig
default:
return false
}
}, 2*time.Second, 10*time.Millisecond, "Signal %s should be received within timeout", sig)
}
}