@@ -4,9 +4,11 @@ import (
44 "flag"
55 "fmt"
66 "log"
7+ "net/http"
78 "os"
89 "os/signal"
910 "path/filepath"
11+ "sync"
1012 "syscall"
1113 "time"
1214
@@ -15,9 +17,14 @@ import (
1517 "github.com/mostlygeek/llama-swap/proxy"
1618)
1719
18- var version string = "0"
19- var commit string = "abcd1234"
20- var date = "unknown"
20+ var (
21+ version string = "0"
22+ commit string = "abcd1234"
23+ date string = "unknown"
24+
25+ // Global mutex for server operations
26+ serverMux sync.Mutex
27+ )
2128
2229func main () {
2330 // Define a command-line flag for the port
@@ -58,20 +65,51 @@ func main() {
5865 }
5966 }
6067
68+ // Channel to signal when we want to exit
69+ exitChan := make (chan struct {})
70+
71+ // Set up signal handling
6172 sigChan := make (chan os.Signal , 1 )
6273 signal .Notify (sigChan , syscall .SIGINT , syscall .SIGTERM )
74+
75+ // Handle shutdown signals
6376 go func () {
64- <- sigChan
65- fmt .Println ( "Shutting down llama-swap" )
77+ sig := <- sigChan
78+ fmt .Printf ( "Received signal %v, shutting down... \n " , sig )
6679 proxyManager .Shutdown ()
67- os . Exit ( 0 )
80+ close ( exitChan )
6881 }()
6982
83+ // Start server in main thread
7084 fmt .Println ("llama-swap listening on " + * listenStr )
71- if err := proxyManager .Run (* listenStr ); err != nil {
72- fmt .Printf ("Server error: %v\n " , err )
73- os .Exit (1 )
74- }
85+ go func () {
86+ for {
87+ serverMux .Lock ()
88+ runChan := make (chan error )
89+
90+ // Start server in separate goroutine
91+ go func () {
92+ runChan <- proxyManager .Run (* listenStr )
93+ }()
94+
95+ // Wait for server to complete or config reload
96+ err = <- runChan
97+
98+ if err != nil && err != http .ErrServerClosed {
99+ fmt .Printf ("Fatal server error: %v\n " , err )
100+ serverMux .Unlock ()
101+ close (exitChan )
102+ return
103+ }
104+
105+ serverMux .Unlock ()
106+ // If we get here, it's because of a normal shutdown (config reload)
107+ // Just continue the loop to let the new server start
108+ }
109+ }()
110+
111+ // Wait for exit signal
112+ <- exitChan
75113}
76114
77115// watchConfigFile monitors the configuration file for changes and recreates the ProxyManager with the new config.
@@ -91,15 +129,13 @@ func watchConfigFile(configPath string, pm **proxy.ProxyManager, listenStr *stri
91129
92130 log .Printf ("Watching config file for changes: %s" , configPath )
93131
94- // Debounce timer
95132 var debounceTimer * time.Timer
96- debounceDuration := 2 * time .Second // Adjust as needed
133+ debounceDuration := 2 * time .Second
97134
98135 for {
99136 select {
100137 case event , ok := <- watcher .Events :
101138 if ! ok {
102- log .Println ("File watcher channel closed." )
103139 return
104140 }
105141 // We only care about writes to the specific config file
@@ -119,18 +155,20 @@ func watchConfigFile(configPath string, pm **proxy.ProxyManager, listenStr *stri
119155 }
120156
121157 // Stop and cleanup old ProxyManager
158+ serverMux .Lock ()
122159 oldPM := * pm
123- log .Println ("Shutting down old ProxyManager instance before reload..." )
124160 oldPM .Shutdown ()
125161
126162 // Create new ProxyManager with new config
127163 newPM := proxy .New (newConfig )
128164 * pm = newPM
165+ serverMux .Unlock ()
129166
130- // Start serving with new ProxyManager
167+ // Start serving with new ProxyManager (outside the lock)
131168 go func () {
132- if err := newPM .Run (* listenStr ); err != nil {
133- log .Printf ("Server error after config reload: %v" , err )
169+ // Try to start the server
170+ if err := newPM .Run (* listenStr ); err != nil && err != http .ErrServerClosed {
171+ log .Printf ("Error starting server after config reload: %v" , err )
134172 }
135173 }()
136174
0 commit comments