Skip to content

Conversation

@NfNitLoop
Copy link
Contributor

exec.CommandContext() creates a Cmd that will kill its process when its associated Context is cancelled. Unfortunately, that kill signal is only sent to the parent process of that Cmd, which in this case was sh. Child processes can continue to run and use CPU and memory.

If your configured linter takes a while to run and uses a substantial amount of memory (ex: eslint w/ TypeScript), and you have the default lint configuration of linting on textDocument/didChange, then it's quite easy to type quickly enough to forkbomb your laptop and cause it ti run out of its many gigabytes of RAM.

This fixes that by making sure to kill the spawned lint process and all of its children.

Note: This doesn't only fix the case where we run a command within the sh hard-coded in the source, like this:

sh
+-- my_lint_command

But also fixes the case where the lint comand you call delegates to other commands like this:

sh
+-- make
  +-- pnpm
    +-- eslint

exec.CommandContext() creates a Cmd that will kill its process when its
associated Context is cancelled. Unfortunately, that kill signal
is only sent to the parent process of that Cmd, which in this case
was `sh`. Child processes can continue to run and use CPU and memory.

If your configured linter takes a while to run and uses a substantial
amount of memory (ex: eslint w/ TypeScript), and you have the default
lint configuration of linting on textDocument/didChange, then
it's quite easy to type quickly enough to forkbomb your laptop and
cause it ti run out of its many gigabytes of RAM.

This fixes that by making sure to kill the spawned lint process and
all of its children.

Note: This doesn't *only* fix the case where we run a command
within the `sh` hard-coded in the source, like this:

```
sh
+-- my_lint_command
```

But also fixes the case where the lint comand you call delegates to
other commands like this:

```
sh
+-- make
  +-- pnpm
    +-- eslint
```
@mattn
Copy link
Owner

mattn commented Dec 24, 2025

Probably Windows build should be fail.

@NfNitLoop
Copy link
Contributor Author

Ah. Hmmm. If this were Rust I'd know how to do that check at compile-time. I don't use Go that often, though. Is there a way to skip code at compile-time, and only do the process group ID on !Windows?

@NfNitLoop
Copy link
Contributor Author

Aha, build constraints:
https://pkg.go.dev/go/build#hdr-Build_Constraints

Working on it!

@NfNitLoop
Copy link
Contributor Author

Fixed. If there's a better way to do this in Go, let me know and I'll do it however you prefer. 😊

@mattn
Copy link
Owner

mattn commented Dec 24, 2025

Well, killableCommand is not called from anywhere.

@mattn mattn merged commit 099aad8 into mattn:master Dec 24, 2025
3 checks passed
@mattn
Copy link
Owner

mattn commented Dec 24, 2025

Thank you

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.

2 participants