Skip to content

Child output is reported only when our shutdown hook wins the race — forward it from the buffer instead #5

Description

@fdaviddpt

Follow-up to #3 / #4, found while verifying the fix against a real host application rather than the test fixture.

What works

The seal holds. A test that echoes and then dies no longer puts a byte on the transport, and the call returns in ~0.1-0.3s instead of waiting out the client's 300s timeout. Verified against a DVSI workspace, which is where #3 was found.

What does not

The other half of #4 — reporting what the dying child printed, under the echo key, with a message naming the fatal — never fires there. The crash still reports the parent's fallback:

phpunit child produced no result (fatal error or crash in the forked test process)

Why

sealChildStdout() ships the buffer from a register_shutdown_function hook registered after the fork. The host framework registers its own shutdown handler during bootstrap — which happens in the daemon before the fork, so the child inherits it ahead of ours in the queue. In DVSI that handler is FwkApi::managePHPError, the very thing that renders the HTML error page; it runs first and ends the process, and our hook never executes.

The integration test passes because its fixture project bootstraps nothing and registers no handler. So the test proves the mechanism, not the environment — worth remembering when reading it.

Proposed fix

Stop depending on shutdown ordering. The ob_start callback already runs on every flush; have it forward each chunk to the result socket as a framed side-channel message instead of discarding it, and let the parent collect those alongside the result payload.

Then output reaches the parent at the moment it is produced, before any shutdown handler exists to lose the race — and a child killed outright (SIGKILL, segfault) still loses only what it had not yet flushed, rather than everything.

The parent already distinguishes "payload received" from "no payload"; this adds "no payload, but here is what it printed", which is the case that matters for debugging a crash.

Note for whoever picks this up

The regression test should grow a fixture that registers a shutdown handler of its own before the run, so the ordering assumption is exercised rather than assumed. That is the gap that let this ship.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions