Skip to content

Commit c36d1da

Browse files
committed
cmd/sketch: add a process reaper
When we're PID 1 in a container, reaping zombies is our responsibility, which we were shirking. No longer! Now we 🥷 all the 🧟 into ☠️, and not 🐢 either.
1 parent 9224eb0 commit c36d1da

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

cmd/sketch/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,14 @@ func setupAndRunAgent(ctx context.Context, flags CLIFlags, modelURL, apiKey, pub
615615
os.Setenv("SKETCH_PUB_KEY", pubKey)
616616
}
617617

618+
if inInsideSketch {
619+
// Start a process reaper, because we are PID 1.
620+
err := startReaper(context.WithoutCancel(ctx))
621+
if err != nil {
622+
slog.WarnContext(ctx, "failed to start process reaper", "error", err)
623+
}
624+
}
625+
618626
wd, err := os.Getwd()
619627
if err != nil {
620628
return err

cmd/sketch/reaper_linux.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//go:build linux
2+
3+
package main
4+
5+
import (
6+
"context"
7+
"log/slog"
8+
"os"
9+
"os/signal"
10+
11+
"golang.org/x/sys/unix"
12+
)
13+
14+
func startReaper(ctx context.Context) error {
15+
if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil {
16+
return err
17+
}
18+
go reapZombies(ctx)
19+
return nil
20+
}
21+
22+
// reapZombies runs until ctx is cancelled.
23+
func reapZombies(ctx context.Context) {
24+
sig := make(chan os.Signal, 16)
25+
signal.Notify(sig, unix.SIGCHLD)
26+
27+
for range sig {
28+
Reap:
29+
for {
30+
var status unix.WaitStatus
31+
pid, err := unix.Wait4(-1, &status, unix.WNOHANG, nil)
32+
switch {
33+
case pid > 0:
34+
slog.DebugContext(ctx, "reaper ran", "pid", pid, "exit", status.ExitStatus())
35+
case err == unix.EINTR:
36+
// interrupted: fall through to retry
37+
case err == unix.ECHILD || pid == 0:
38+
break Reap // no more ready children; wait for next SIGCHLD
39+
default:
40+
slog.WarnContext(ctx, "wait4 error", "error", err)
41+
break Reap
42+
}
43+
}
44+
}
45+
}

cmd/sketch/reaper_other.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !linux
2+
3+
package main
4+
5+
import "context"
6+
7+
func startReaper(_ context.Context) error {
8+
return nil
9+
}

0 commit comments

Comments
 (0)