Close UART gracefully on SIGINT/SIGTERM in goTool.
Go exits on Ctrl+C without running defers, leaving the serial port locked; register shutdown hooks for serve and CLI commands. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0eea27a876
commit
e4ce18edd8
@ -62,6 +62,8 @@ func runTest(portOverride string, baudOverride int, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open %s: %w", port, err)
|
return fmt.Errorf("open %s: %w", port, err)
|
||||||
}
|
}
|
||||||
|
registerShutdown(func() { _ = sp.Close() })
|
||||||
|
enableShutdownOnInterrupt()
|
||||||
defer sp.Close()
|
defer sp.Close()
|
||||||
|
|
||||||
if !*verbose {
|
if !*verbose {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@ -34,21 +35,29 @@ func runServe(portName string, baud int, args []string) error {
|
|||||||
|
|
||||||
link := newManagedSerial(portName, baud)
|
link := newManagedSerial(portName, baud)
|
||||||
link.quiet = true
|
link.quiet = true
|
||||||
defer link.Close()
|
|
||||||
|
|
||||||
hub := newWSHub()
|
hub := newWSHub()
|
||||||
streamCtl := newAccelStreamCtl()
|
streamCtl := newAccelStreamCtl()
|
||||||
tapCtl := newTapNotifyCtl()
|
tapCtl := newTapNotifyCtl()
|
||||||
stop := make(chan struct{})
|
stop := make(chan struct{})
|
||||||
defer close(stop)
|
|
||||||
|
var dashSrv *http.Server
|
||||||
|
var apiSrv *http.Server
|
||||||
|
registerShutdown(func() {
|
||||||
|
close(stop)
|
||||||
|
shutdownHTTPServer(dashSrv)
|
||||||
|
shutdownAPIServer(apiSrv)
|
||||||
|
if err := link.Close(); err != nil {
|
||||||
|
log.Printf("UART close: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
go runPoller(link, portName, hub, streamCtl, tapCtl, *interval, stop)
|
go runPoller(link, portName, hub, streamCtl, tapCtl, *interval, stop)
|
||||||
go runBatteryPoller(link, hub, 5*time.Second, stop)
|
go runBatteryPoller(link, hub, 5*time.Second, stop)
|
||||||
go runCacheStatusDashboardPoller(link, hub, *accelInterval, stop)
|
go runCacheStatusDashboardPoller(link, hub, *accelInterval, stop)
|
||||||
|
|
||||||
var apiSrv *http.Server
|
|
||||||
if *apiAddr != "" {
|
if *apiAddr != "" {
|
||||||
apiSrv = runAPIServer(portName, link, *apiAddr, *accelInterval, hub, streamCtl, tapCtl, stop)
|
apiSrv = runAPIServer(portName, link, *apiAddr, *accelInterval, hub, streamCtl, tapCtl, stop)
|
||||||
defer shutdownAPIServer(apiSrv)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
@ -81,5 +90,14 @@ func runServe(portName string, baud int, args []string) error {
|
|||||||
if *apiAddr == "" {
|
if *apiAddr == "" {
|
||||||
log.Printf("external API disabled (-api-addr \"\")")
|
log.Printf("external API disabled (-api-addr \"\")")
|
||||||
}
|
}
|
||||||
return http.ListenAndServe(*addr, mux)
|
|
||||||
|
dashSrv = &http.Server{Addr: *addr, Handler: mux}
|
||||||
|
go func() {
|
||||||
|
if err := dashSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Printf("dashboard server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
waitForShutdown()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,8 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("open serial: %v", err)
|
log.Fatalf("open serial: %v", err)
|
||||||
}
|
}
|
||||||
|
registerShutdown(func() { _ = sp.Close() })
|
||||||
|
enableShutdownOnInterrupt()
|
||||||
defer sp.Close()
|
defer sp.Close()
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "version":
|
case "version":
|
||||||
|
|||||||
75
goTool/shutdown.go
Normal file
75
goTool/shutdown.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
shutdownMu sync.Mutex
|
||||||
|
shutdownFns []func()
|
||||||
|
hooksOnce sync.Once
|
||||||
|
bgHandlerOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// registerShutdown runs fn on SIGINT/SIGTERM (LIFO order).
|
||||||
|
func registerShutdown(fn func()) {
|
||||||
|
shutdownMu.Lock()
|
||||||
|
shutdownFns = append(shutdownFns, fn)
|
||||||
|
shutdownMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runShutdownHooks() {
|
||||||
|
hooksOnce.Do(func() {
|
||||||
|
shutdownMu.Lock()
|
||||||
|
fns := shutdownFns
|
||||||
|
shutdownMu.Unlock()
|
||||||
|
for i := len(fns) - 1; i >= 0; i-- {
|
||||||
|
fns[i]()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableShutdownOnInterrupt listens for SIGINT/SIGTERM in the background and exits
|
||||||
|
// after running shutdown hooks. Use for one-shot CLI commands (OTA, etc.).
|
||||||
|
func enableShutdownOnInterrupt() {
|
||||||
|
bgHandlerOnce.Do(func() {
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
sig := <-ch
|
||||||
|
signal.Stop(ch)
|
||||||
|
log.Printf("received %v, shutting down…", sig)
|
||||||
|
runShutdownHooks()
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForShutdown blocks until SIGINT/SIGTERM, runs hooks, and returns.
|
||||||
|
// Use for long-running servers (serve/dashboard).
|
||||||
|
func waitForShutdown() {
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
|
||||||
|
sig := <-ch
|
||||||
|
signal.Stop(ch)
|
||||||
|
log.Printf("received %v, shutting down…", sig)
|
||||||
|
runShutdownHooks()
|
||||||
|
}
|
||||||
|
|
||||||
|
func shutdownHTTPServer(srv *http.Server) {
|
||||||
|
if srv == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
|
log.Printf("HTTP shutdown: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user