mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
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:
@@ -125,7 +125,7 @@ func main() {
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go func() {
|
go func() {
|
||||||
// Capture cancel for interrupt
|
// Capture cancel for interrupt
|
||||||
signaler.WaitForInterrupt()
|
<-signaler.WaitForInterrupt()
|
||||||
cancel()
|
cancel()
|
||||||
fmt.Println("rpc process interrupted")
|
fmt.Println("rpc process interrupted")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func main() {
|
|||||||
fmt.Printf("Could not stop task %v %v. Error: %v\n", bt.MetaData.ID, bt.MetaData.Strategy, err)
|
fmt.Printf("Could not stop task %v %v. Error: %v\n", bt.MetaData.ID, bt.MetaData.Strategy, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
interrupt := signaler.WaitForInterrupt()
|
interrupt := <-signaler.WaitForInterrupt()
|
||||||
log.Infof(log.Global, "Captured %v, shutdown requested\n", interrupt)
|
log.Infof(log.Global, "Captured %v, shutdown requested\n", interrupt)
|
||||||
log.Infoln(log.Global, "Exiting.")
|
log.Infoln(log.Global, "Exiting.")
|
||||||
err = bt.Stop()
|
err = bt.Stop()
|
||||||
@@ -230,7 +230,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.Infoln(log.GRPCSys, "Ready to receive commands")
|
log.Infoln(log.GRPCSys, "Ready to receive commands")
|
||||||
}(btCfg)
|
}(btCfg)
|
||||||
interrupt := signaler.WaitForInterrupt()
|
interrupt := <-signaler.WaitForInterrupt()
|
||||||
log.Infof(log.Global, "Captured %v, shutdown requested\n", interrupt)
|
log.Infof(log.Global, "Captured %v, shutdown requested\n", interrupt)
|
||||||
if btCfg.StopAllTasksOnClose {
|
if btCfg.StopAllTasksOnClose {
|
||||||
log.Infoln(log.Global, "Stopping all running tasks on close")
|
log.Infoln(log.Global, "Stopping all running tasks on close")
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ func main() {
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go func() {
|
go func() {
|
||||||
// Capture cancel for interrupt
|
// Capture cancel for interrupt
|
||||||
signaler.WaitForInterrupt()
|
<-signaler.WaitForInterrupt()
|
||||||
cancel()
|
cancel()
|
||||||
fmt.Println("rpc process interrupted")
|
fmt.Println("rpc process interrupted")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -157,7 +157,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func waitForInterrupt(waiter chan<- struct{}) {
|
func waitForInterrupt(waiter chan<- struct{}) {
|
||||||
interrupt := signaler.WaitForInterrupt()
|
interrupt := <-signaler.WaitForInterrupt()
|
||||||
gctlog.Infof(gctlog.Global, "Captured %v, shutdown requested.\n", interrupt)
|
gctlog.Infof(gctlog.Global, "Captured %v, shutdown requested.\n", interrupt)
|
||||||
waiter <- struct{}{}
|
waiter <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package signaler provides cross-platform signal handling for graceful application shutdown
|
||||||
package signaler
|
package signaler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,20 +7,9 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var s = make(chan os.Signal, 1)
|
// WaitForInterrupt returns a channel to receive termination signals
|
||||||
|
func WaitForInterrupt() chan os.Signal {
|
||||||
func init() {
|
c := make(chan os.Signal, 1)
|
||||||
sigs := []os.Signal{
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
os.Interrupt,
|
return c
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
37
signaler/signaler_test.go
Normal file
37
signaler/signaler_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user