@@ -5,16 +5,12 @@ import (
55 "errors"
66 "fmt"
77 "io"
8- "log/slog"
98 "net"
109 "os"
1110 "path/filepath"
12- "time"
1311
1412 "github.com/cenkalti/rpc2"
1513 "github.com/cenkalti/rpc2/jsonrpc"
16- "github.com/fsnotify/fsnotify"
17- "github.com/prometheus/procfs"
1814)
1915
2016const (
@@ -92,104 +88,5 @@ func Cleanup(binary string) error {
9288}
9389
9490func (c * Client ) Exit (ctx context.Context , binary string ) error {
95- pidPath := fmt .Sprintf ("%s/%s.pid" , RUN_DIR , binary )
96- ctlPattern := fmt .Sprintf ("%s/%s.*.ctl" , RUN_DIR , binary )
97-
98- watcher , err := fsnotify .NewWatcher ()
99- if err != nil {
100- return fmt .Errorf ("failed to create file watcher: %w" , err )
101- }
102- defer watcher .Close ()
103-
104- err = watcher .Add (RUN_DIR )
105- if err != nil {
106- return fmt .Errorf ("failed to watch directory %s: %w" , RUN_DIR , err )
107- }
108-
109- err = c .CallWithContext (ctx , "exit" , []string {}, nil )
110- if err != nil {
111- return fmt .Errorf ("failed to send exit command: %w" , err )
112- }
113-
114- timeout := time .After (30 * time .Second )
115-
116- pidRemoved := false
117- ctlRemoved := false
118-
119- for {
120- select {
121- case event , ok := <- watcher .Events :
122- if ! ok {
123- return errors .New ("watcher channel closed" )
124- }
125- if event .Op & fsnotify .Remove == fsnotify .Remove {
126- if event .Name == pidPath {
127- pidRemoved = true
128- slog .Info ("pid file removed" , "pid_file" , pidPath )
129- } else if matched , _ := filepath .Match (ctlPattern , event .Name ); matched {
130- ctlRemoved = true
131- slog .Info ("ctl file removed" , "ctl_file" , event .Name )
132- }
133-
134- if pidRemoved && ctlRemoved {
135- slog .Info ("files removed, waiting for hugepages to be available" )
136-
137- // Get initial hugepages state
138- fs , err := procfs .NewDefaultFS ()
139- if err != nil {
140- slog .Warn ("cannot access procfs, skipping hugepages check" , "error" , err )
141- return nil
142- }
143-
144- // Poll until hugepages are available
145- ticker := time .NewTicker (50 * time .Millisecond )
146- defer ticker .Stop ()
147- hugepagesTimeout := time .After (5 * time .Second )
148-
149- for {
150- select {
151- case <- ticker .C :
152- memInfo , err := fs .Meminfo ()
153- if err != nil {
154- slog .Warn ("cannot read meminfo, assuming process exited" , "error" , err )
155- return nil
156- }
157-
158- // Check if there are free hugepages
159- if memInfo .HugePagesFree != nil && * memInfo .HugePagesFree > 0 {
160- slog .Info ("hugepages available, process fully exited" ,
161- "hugepages_free" , * memInfo .HugePagesFree )
162- return nil
163- }
164-
165- // Also check if no hugepages are configured
166- if memInfo .HugePagesTotal != nil && * memInfo .HugePagesTotal == 0 {
167- slog .Info ("no hugepages configured, process fully exited" )
168- return nil
169- }
170-
171- slog .Debug ("waiting for hugepages to be freed" ,
172- "hugepages_total" , memInfo .HugePagesTotal ,
173- "hugepages_free" , memInfo .HugePagesFree )
174-
175- case <- hugepagesTimeout :
176- slog .Warn ("timeout waiting for hugepages, proceeding anyway" )
177- return nil
178- case <- ctx .Done ():
179- return ctx .Err ()
180- }
181- }
182- }
183- }
184- case err , ok := <- watcher .Errors :
185- if ! ok {
186- return errors .New ("watcher error channel closed" )
187- }
188- return fmt .Errorf ("watcher error: %w" , err )
189- case <- timeout :
190- return fmt .Errorf ("timeout waiting for process to exit (pid_removed=%v, ctl_removed=%v)" , pidRemoved , ctlRemoved )
191- case <- ctx .Done ():
192- return ctx .Err ()
193- }
194- }
91+ return c .CallWithContext (ctx , "exit" , []string {}, nil )
19592}
0 commit comments