Skip to content

fix(process): reap entire process group on cmd.Cancel - #39143

Merged
bircni merged 11 commits into
go-gitea:mainfrom
rremer:main-orphanage
Aug 29, 2026
Merged

fix(process): reap entire process group on cmd.Cancel#39143
bircni merged 11 commits into
go-gitea:mainfrom
rremer:main-orphanage

Conversation

@rremer

@rremer rremer commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

I ran into an issue where many client disconnects during clone operations were leaving expensive git pack-objects --revs --thin --stdout --delta-base-offset processes in gitea. These processes would still run to completion, but the underlying http request which initiated them had long since been cancelled.

The bug is subtle, but the cmd.Cancel call would target the original PID, while git commands regularly fork several child processes which are not cancelled. Because their parent was cancelled, they'd get reparented to either the gitea process or pid 1 depending on the kernel.

This change uses the negative process group to cancel the entire process group together instead of just the parent.

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Aug 26, 2026
Comment thread modules/process/manager_unix.go Outdated
@wxiaoguang
wxiaoguang marked this pull request as draft August 26, 2026 18:32
Comment thread modules/process/manager_unix_test.go Outdated
@silverwind

Copy link
Copy Markdown
Member

Please trim down on comments.

…arent

Assisted-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Royce Remer <royceremer@gmail.com>
Comment thread modules/process/manager_unix_test.go Outdated
@wxiaoguang

Copy link
Copy Markdown
Contributor

What do you think about this bc21fdd ?

@wxiaoguang
wxiaoguang marked this pull request as ready for review August 27, 2026 10:18
wxiaoguang and others added 2 commits August 27, 2026 18:38
…escalate SIGINT -> SIGKILL

Assisted-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Royce Remer <royceremer@gmail.com>
@rremer

rremer commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

@wxiaoguang while a much bigger refactor, your change will be safer because mine relied on callers to use setSysProcAttribute(), whereas now it would be the default behavior. The move to SIGTERM is also preferable, however I believe it introduced a new bug: if the child ignores SIGTERM it could hang forever, and grandchildren would never get a signal.

I've added a few test cases to prove this, and my proposed fix is just to introduce a default 10s (configurable) wait on Cancel() before escalating to SIGKILL: 9e336b1

@wxiaoguang

wxiaoguang commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

however I believe it introduced a new bug: if the child ignores SIGTERM it could hang forever, and grandchildren would never get a signal.

I've added a few test cases to prove this, and my proposed fix is just to introduce a default 10s (configurable) wait on Cancel() before escalating to SIGKILL: 9e336b1

I intentionally didn't do that because it will just cause pid data-race problems. You can ask AI about the PID-race details.

So I strongly prefer to revert 9e336b1 (Actually, I mean, it needs to be reverted)

And, I don't see why the SIGTERM would be ignored in real world for our cases. If it would happen, I already prepared WithOnCancelForceKill

Comment thread modules/process/command_unix.go Outdated
@wxiaoguang
wxiaoguang marked this pull request as draft August 27, 2026 17:19
Assisted-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Royce Remer <royceremer@gmail.com>
@rremer

rremer commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

pid data-race problems.

Good point, although technically SIGINT suffers from this as well.

I don't see why the SIGTERM would be ignored in real world for our cases

Maybe not 'ignored' in real-world scenarios, but certainly blocked longer than what we might be willing to wait. How long is too-long is really only known by the caller, but I could see scenarios where that external renderer or ssh call sites hang on some connection. Is 5 minutes too-long? Is 30 seconds?

It looks like ProcessState can be trusted while we hold a reference, how about we just check it before doing any syscall? 3b7449f

@wxiaoguang

Copy link
Copy Markdown
Contributor

I don't see why the SIGTERM would be ignored in real world for our cases

Maybe not 'ignored' in real-world scenarios, but certainly blocked longer than what we might be willing to wait. How long is too-long is really only known by the caller, but I could see scenarios where that external renderer or ssh call sites hang on some connection. Is 5 minutes too-long? Is 30 seconds?

It looks like ProcessState can be trusted while we hold a reference, how about we just check it before doing any syscall? 3b7449f

I don't see it would really affect real world use cases. And there is no easy solution for the PID reuse data-race.

I believe we can take the graceful kill (SIGTERM) as default behavior, if anything wrong, just switch to SIGKILL.

@wxiaoguang

wxiaoguang commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

It looks like ProcessState can be trusted while we hold a reference, how about we just check it before doing any syscall? 3b7449f

No, it's not right. "there is no easy solution for the PID reuse data-race."

@rremer

rremer commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

I don't see it would really affect real world use cases

I think the real-world use-case is pretty theoretical, for sure, but this would reduce the window of pid reuse from however long since the process was started, to probably nanoseconds.

@wxiaoguang
wxiaoguang marked this pull request as ready for review August 27, 2026 18:39
@wxiaoguang

wxiaoguang commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

I don't see it would really affect real world use cases

I think the real-world use-case is pretty theoretical, for sure, but this would reduce the window of pid reuse from however long since the process was started, to probably nanoseconds.

Let's just try to use SIGTERM as the default signal in production to see whether there would be real problems. If yes, just switch to SIGKILL.

@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Aug 27, 2026
@bircni
bircni requested a review from silverwind August 28, 2026 20:59
@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Aug 29, 2026
@bircni
bircni enabled auto-merge (squash) August 29, 2026 19:36
@bircni bircni added the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Aug 29, 2026
@bircni
bircni merged commit eea0367 into go-gitea:main Aug 29, 2026
24 checks passed
@GiteaBot GiteaBot added this to the 28.0.0 milestone Aug 29, 2026
@GiteaBot GiteaBot removed the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Aug 29, 2026
silverwind added a commit to silverwind/gitea that referenced this pull request Aug 30, 2026
* origin/main: (30 commits)
  docs: Update CHANGELOG for release 1.27.3 (go-gitea#39170)
  [skip ci] Updated translations via Crowdin
  [skip ci] Updated translations via Crowdin
  fix(process): reap entire process group on cmd.Cancel (go-gitea#39143)
  feat(web): Add org removal functionality to admin user details page (go-gitea#38013)
  fix(actions): run every due schedule exactly once per occurrence (go-gitea#39078)
  refactor: pagination/pager (go-gitea#39162)
  [skip ci] Updated translations via Crowdin
  enhance(actions): make workflow dispatch choice dropdown support search (go-gitea#39154)
  fix(web): populate the reason for "cannot commit to branch" in web editor commit form (go-gitea#39155)
  refactor(automerge): fix error handling, populate recent automerge tasks on restart (go-gitea#39001)
  chore(frontend): avoid loading CSS twice in vite dev mode (go-gitea#39160)
  fix(packages): preserve SemVer prerelease identifiers in Swift Registry (go-gitea#39156)
  [skip ci] Updated translations via Crowdin
  ci(snap): pack snaps without an LXD container (go-gitea#39152)
  chore: apply golangci "forbidigo" to all packages (go-gitea#39151)
  refactor: drop two unmaintained dependencies, rename the byte size helpers (go-gitea#39083)
  fix(actions): keep step-level continue-on-error expressions unevaluated (go-gitea#39141)
  [skip ci] Updated translations via Crowdin
  feat: add deploy tokens (go-gitea#37306)
  ...

# Conflicts:
#	modelmigration/migrations.go
#	modelmigration/v28/v352.go
#	routers/private/hook_pre_receive.go
#	templates/repo/release/new.tmpl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. topic/code-linting type/bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants