Skip to content

Add  KillProcessGroup  option to handle process groups#1313

Open
mkaaad wants to merge 6 commits intomvdan:masterfrom
mkaaad:feat/process-group-cancel
Open

Add  KillProcessGroup  option to handle process groups#1313
mkaaad wants to merge 6 commits intomvdan:masterfrom
mkaaad:feat/process-group-cancel

Conversation

@mkaaad
Copy link
Copy Markdown

@mkaaad mkaaad commented Apr 2, 2026

Reverted the previous change that unconditionally killed the entire process group (main.go and main_test.go were not reverted).

To prevent interactive commands from failing, I add an explicit switch  KillProcessGroup  to the Runner. It defaults to  false  to keep the original behavior. Set it to  true  when user want to ensure all child processes are terminated.

This maintains backward compatibility while solving the orphaned process problem.

// ensure all child processes are terminated
runner.New(interp.KillProcessGroup(true))

mkaaad added 2 commits April 2, 2026 17:59
…oup signals

This builds upon the reapplication of "interp: send interrupt/kill signals to
the whole process group" (mvdan#1244) (excluding cmd/gosh/main.go and
cmd/gosh/main_test.go, which reverts commit e74afc1),
adding a KillProcessGroup RunnerOption (defaulting to false) so users can
opt in to process group behavior, restoring single-process signal delivery
as the default.
- Add CREATE_NEW_PROCESS_GROUP flag in prepareCommand for console events
- Use GenerateConsoleCtrlEvent in interruptCommand before falling back to TerminateJobObject
- Fix process handle management with OpenProcess for proper job assignment
- Clean up job handles automatically after process exit
- Add missing postStartCommand implementations for non-Windows platforms
@mvdan
Copy link
Copy Markdown
Owner

mvdan commented Apr 2, 2026

I admit I don't really feel like maintaining the windows code because I don't understand it nor can I really test it well. Does anyone actually need windows support for this? The original patch only did this for unix.

@mvdan
Copy link
Copy Markdown
Owner

mvdan commented Apr 2, 2026

Also cc @andreynering for thoughts on whether an option like this makes sense. My intuition is that an option here is unintuitive and should be avoided, but if we can't get reasonable behavior for all users without two modes, perhaps it's the best we can do.

@mkaaad
Copy link
Copy Markdown
Author

mkaaad commented Apr 2, 2026

I'm actually trying to fix an issue I encountered with Crush:
https://github.com/charmbracelet/crush

When using this agent to work on a Go project, I ran into a problem where processes couldn’t be terminated.
For example, when I asked the LLM to write a simple server using Gin, it wrote the program and tested the endpoints correctly.
However, after testing, when the LLM tried to kill the process using the job kill tool, it just hung infinitely.

I found that Crush executes commands using:
 runner.Run(ctx, command) 
from:
https://github.com/charmbracelet/crush/blob/main/internal/shell/shell.go

But when a cancel signal is sent to the provided  ctx , the process is not actually killed.

I checked the issues and saw other users are facing the same problem:
charmbracelet/crush#2490
His platform is Windows 11.

So I want to create a patch for users who run into this.
I tested the second Windows-specific patch on my own machine (Windows 10), and it successfully kills  go run  commands that previously couldn’t be terminated.

Since most people hitting this issue are users of the  interp  package,
I think adding an initialization option to the runner would be the most compatible approach.
It would also provide a way for users who really need to ensure child processes are fully killed.

@mkaaad
Copy link
Copy Markdown
Author

mkaaad commented Apr 2, 2026

Also, there’s another related issue in the Crush repo:
charmbracelet/crush#1691

The community previously tried to fix this bug by adding a timeout watcher
charmbracelet/crush#1736
But processes bound to listening ports in the background still wouldn’t terminate properly,
leading to issues like port conflicts and leftover processes.

mkaaad and others added 2 commits April 3, 2026 14:00
- Introduce jobEntry struct with mutex to prevent double-close races
  between killCommand/interruptCommand and the cleanup goroutine
- Fix procHandle lifetime: single OpenProcess with SYNCHRONIZE passed
  to cleanup goroutine, eliminating the second OpenProcess call
- Add PROCESS_SUSPEND_RESUME to OpenProcess flags required by NtResumeProcess
- Replace ResumeThread with NtResumeProcess to resume all threads at once
- Fix CTRL_C_EVENT → CTRL_BREAK_EVENT (CREATE_NEW_PROCESS_GROUP processes
  ignore CTRL_C_EVENT)
- Use LoadAndDelete in interruptCommand to prevent double-terminate races
- Return error from postStartCommand on all platforms; caller in handler.go
  now propagates setup failures instead of silently discarding them

Optimized with assistance from Claude (Crush).
@SmilingWalker
Copy link
Copy Markdown

The same problem when using in crush on Windows 10

@andreynering
Copy link
Copy Markdown
Collaborator

This is an annoying issue, but unfortunately the trade-off of interactive commands not working anymore is a big deal IMO.

I wrote some explanation for the revert here: #1244.

Ideally, we should spend some time investigating a solution that works for all use uses, but I don't have time available for that now.

@mvdan
Copy link
Copy Markdown
Owner

mvdan commented Apr 8, 2026

Echoing what @andreynering said, I think further research needs to happen here because presumably, Bash implements an interactive shell that does the right thing without needing a global option like this. We should ideally do the same.

@mkaaad mkaaad closed this Apr 8, 2026
@mkaaad
Copy link
Copy Markdown
Author

mkaaad commented Apr 8, 2026

How do you feel about combining the process group mode toggle with interp.Interactive(false)? @mvdan @andreynering

mkaaad and others added 2 commits April 8, 2026 23:23
Remove the separate KillProcessGroup option and instead enable process
group killing automatically when Interactive is false (non-interactive mode).
@mkaaad mkaaad reopened this Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants