Skip to content

fix dead forked pthread_fork children#2332

Draft
henderkes wants to merge 2 commits intomainfrom
fix/pcntl_fork
Draft

fix dead forked pthread_fork children#2332
henderkes wants to merge 2 commits intomainfrom
fix/pcntl_fork

Conversation

@henderkes
Copy link
Copy Markdown
Contributor

closes #2331

@henderkes henderkes requested review from AlliBalliBaba and withinboredom and removed request for AlliBalliBaba April 6, 2026 12:25
@withinboredom
Copy link
Copy Markdown
Member

I'm not sure this actually addresses the issue. The reporter said forking caused it, but then doesn't seem to be actually forking in their code where the issue comes from and only provided a forking reproducer. I think we should wait until the reporter (or someone else) confirms whether it is forking or something else entirely.

It would be worth getting to the root cause of why threads hang around after a fork instead of just exiting. Shutdown functions should still run IIRC, among other things.

@henderkes
Copy link
Copy Markdown
Contributor Author

I'm not sure this actually addresses the issue.

Maybe not the one in the issue, but it definitely solves a deadlock issue either way. Not that people should fork in a http context, but if they do it should clean up.

It would be worth getting to the root cause of why threads hang around after a fork instead of just exiting. Shutdown functions should still run IIRC, among other things.

There are multiple paths where it could get stuck:

zend_first_try {
    char *scriptName = NULL;
    while ((scriptName = go_frankenphp_before_script_execution(thread_index))) {

being one, where it waits for the next request to come in. which never happens because the Go threads aren't forked into the child process. But it would deadlock or crash either way once it attempts to do any Go runtime scheduling or cleanup. Forcing a hard exit is the only thing to sidestep go's issues there that I can think of.

We can _exit out after php shutdown handlers (just move it to just before go_frankenphp_after_script_execution, but I'm not sure that's actually correct.

@AlliBalliBaba
Copy link
Copy Markdown
Contributor

Seems strange that forking would happen in a HTTP context in the first place. Shouldn't FPM have the same problem as it also executes requests in a loop in these child processes?

@henderkes
Copy link
Copy Markdown
Contributor Author

Seems strange that forking would happen in a HTTP context in the first place. Shouldn't FPM have the same problem as it also executes requests in a loop in these child processes?

I've not looked at the code extensively, but it seems that the child processes exit and show up as zombies, rather than sleeping (waiting for the next request). They should be reaped at some point, but even if they aren't. they don't consume resources anymore.

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.

pcntl_fork() in worker thread creates full Go runtime copy that never exits, causing unbounded process accumulation

3 participants