@@ -11,13 +11,11 @@ import (
1111 "net/http"
1212 "net/url"
1313 "os"
14- "os/signal"
1514 "path/filepath"
1615 "slices"
1716 "sort"
1817 "strings"
1918 "sync"
20- "syscall"
2119 "time"
2220 "unicode"
2321
@@ -672,59 +670,31 @@ func (s *Server) cleanupPIDFile() {
672670 }
673671}
674672
675- // HandleSignals sets up signal handlers for:
676- // - SIGTERM, SIGINT, SIGHUP: save conversation state, then close the process
677- // - SIGUSR1: save conversation state without exiting
678- func (s * Server ) HandleSignals (ctx context.Context , process * termexec.Process ) {
679- // Handle shutdown signals (SIGTERM, SIGINT, SIGHUP)
680- shutdownCh := make (chan os.Signal , 1 )
681- signal .Notify (shutdownCh , os .Interrupt , syscall .SIGTERM , syscall .SIGHUP )
682- go func () {
683- sig := <- shutdownCh
684- s .logger .Info ("Received shutdown signal, saving state before closing process" , "signal" , sig )
685-
686- // Save conversation state if configured (synchronously before closing process)
687- if s .statePersistenceCfg .SaveState && s .statePersistenceCfg .StateFile != "" {
688- if err := s .conversation .SaveState (s .conversation .Messages (), s .statePersistenceCfg .StateFile ); err != nil {
689- s .logger .Error ("Failed to save conversation state on signal" , "signal" , sig , "error" , err )
690- } else {
691- s .logger .Info ("Saved conversation state on signal" , "signal" , sig , "stateFile" , s .statePersistenceCfg .StateFile )
692- }
693- }
673+ // saveAndCleanup saves the conversation state and cleans up before shutdown
674+ func (s * Server ) saveAndCleanup (sig os.Signal , process * termexec.Process ) {
675+ // Save conversation state if configured (synchronously before closing process)
676+ s .saveStateIfConfigured (sig .String ())
694677
695- // Clean up PID file
696- s .cleanupPIDFile ()
678+ // Clean up PID file
679+ s .cleanupPIDFile ()
697680
698- // Now close the process
699- if err := process .Close (s .logger , 5 * time .Second ); err != nil {
700- s .logger .Error ("Error closing process" , "signal" , sig , "error" , err )
701- }
702- }()
681+ // Now close the process
682+ if err := process .Close (s .logger , 5 * time .Second ); err != nil {
683+ s .logger .Error ("Error closing process" , "signal" , sig , "error" , err )
684+ }
685+ }
703686
704- // Handle SIGUSR1 for save without exit
705- saveOnlyCh := make (chan os.Signal , 1 )
706- signal .Notify (saveOnlyCh , syscall .SIGUSR1 )
707- go func () {
708- for {
709- select {
710- case <- saveOnlyCh :
711- s .logger .Info ("Received SIGUSR1, saving state without exiting" )
712-
713- // Save conversation state if configured
714- if s .statePersistenceCfg .SaveState && s .statePersistenceCfg .StateFile != "" {
715- if err := s .conversation .SaveState (s .conversation .Messages (), s .statePersistenceCfg .StateFile ); err != nil {
716- s .logger .Error ("Failed to save conversation state on SIGUSR1" , "error" , err )
717- } else {
718- s .logger .Info ("Saved conversation state on SIGUSR1" , "stateFile" , s .statePersistenceCfg .StateFile )
719- }
720- } else {
721- s .logger .Warn ("SIGUSR1 received but state saving is not configured" )
722- }
723- case <- ctx .Done ():
724- return
725- }
687+ // saveStateIfConfigured saves the conversation state if configured
688+ func (s * Server ) saveStateIfConfigured (source string ) {
689+ if s .statePersistenceCfg .SaveState && s .statePersistenceCfg .StateFile != "" {
690+ if err := s .conversation .SaveState (s .conversation .Messages (), s .statePersistenceCfg .StateFile ); err != nil {
691+ s .logger .Error ("Failed to save conversation state" , "source" , source , "error" , err )
692+ } else {
693+ s .logger .Info ("Saved conversation state" , "source" , source , "stateFile" , s .statePersistenceCfg .StateFile )
726694 }
727- }()
695+ } else {
696+ s .logger .Warn ("Save requested but state saving is not configured" , "source" , source )
697+ }
728698}
729699
730700// registerStaticFileRoutes sets up routes for serving static files
0 commit comments