Skip to content

extra pthread handling#2390

Open
henderkes wants to merge 4 commits into
mainfrom
feat/fork-support
Open

extra pthread handling#2390
henderkes wants to merge 4 commits into
mainfrom
feat/fork-support

Conversation

@henderkes
Copy link
Copy Markdown
Contributor

unlikely to be needed, but better safe than sorry

unlikely to be needed, but better safe than sorry
Comment thread frankenphp.c
static void frankenphp_fork_child(void) {
is_forked_child = true;
#ifdef __linux__
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's an equivalent to this in macOS or BSD?

realistically speaking, it's hard to imagine this ever kicks in if a php object destructor waits on the child pid, but in case the php runtime throws an unrecoverable error in a destructor, it might exit the thread without calling the destructor responsible for signalling and cleaning up the child

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this syscall can race. Here is a workaround: https://stackoverflow.com/a/59216119

On FreeBSD, you can do this:

#include <sys/procctl.h>
#include <signal.h>

int sig = SIGKILL;
// P_PID means we are targeting a process ID. 
// 0 means "the current calling process".
procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sig);

On macOS, it's harder; you have to run kqueue and way more code.

@henderkes henderkes force-pushed the feat/fork-support branch from cdb8a37 to 1f2963e Compare May 4, 2026 07:34
@henderkes henderkes marked this pull request as ready for review May 12, 2026 08:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens FrankenPHP’s behavior around fork() in non-Windows builds to avoid unsafe interactions between forked PHP children and the Go-backed SAPI I/O layer, and tweaks the Linux CI workflow to ensure libbrotli-dev is present/reinstalled.

Changes:

  • Mark forked children via pthread_atfork() and (on Linux) attempt to set PR_SET_PDEATHSIG to kill the child if its parent dies.
  • Prevent forked children from calling Go-backed SAPI output/header/flush hooks (ub_write, send_headers, sapi_flush).
  • Add an early apt-get update + libbrotli-dev reinstall step in the Linux test workflow.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
frankenphp.c Adds Linux PDEATHSIG in the atfork child handler and disables Go-backed SAPI I/O paths in forked children.
.github/workflows/tests.yaml Adds an extra dependencies install step (apt update + reinstall libbrotli-dev) ahead of building watcher.
Comments suppressed due to low confidence (1)

.github/workflows/tests.yaml:66

  • This adds an apt-get update + libbrotli-dev reinstall step, but the job already has a later 'Reinstall libbrotli-dev' workaround step. Consider merging these (e.g., do the update once and keep a single reinstall at the point it’s needed) to avoid redundant package operations and extra CI time.
      - name: Install system dependencies
        run: sudo apt-get update && sudo apt-get install --reinstall -y libbrotli-dev
      - name: Install e-dant/watcher
        uses: ./.github/actions/watcher
      - name: Reinstall libbrotli-dev
        # TODO: remove this workaround when fixed upstream
        run: sudo apt-get install --reinstall -y libbrotli-dev
      - name: Set CGO flags

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frankenphp.c
static void frankenphp_fork_child(void) {
is_forked_child = true;
#ifdef __linux__
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
Comment thread .github/workflows/tests.yaml Outdated
Comment thread frankenphp.c
static void frankenphp_fork_child(void) {
is_forked_child = true;
#ifdef __linux__
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this syscall can race. Here is a workaround: https://stackoverflow.com/a/59216119

On FreeBSD, you can do this:

#include <sys/procctl.h>
#include <signal.h>

int sig = SIGKILL;
// P_PID means we are targeting a process ID. 
// 0 means "the current calling process".
procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &sig);

On macOS, it's harder; you have to run kqueue and way more code.

Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
Signed-off-by: Marc <m@pyc.ac>
@henderkes
Copy link
Copy Markdown
Contributor Author

I'll fix this up later - the BSD path is not equivalent to the linux one though. We optimally want the parent thread dying to trigger the signal, not the parent process. In reality they'd be the same if a user doesn't do silly things in php, though.

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.

3 participants