Skip to content

v0.3.1

Choose a tag to compare

@arloliu arloliu released this 03 Aug 11:02
· 26 commits to main since this release

Closes the stderr-loss window v0.3.0
shipped with and named in its own changelog.

Fixed

The last stderr a crashing plugin writes is no longer lost when the plugin
exits in the same instant it prints. PluginCrashError.StderrTail, and the
stderr suffix inside Reason, could come back empty for a plugin that had in
fact printed — indistinguishable from one that printed nothing, which is the
exact ambiguity the tail exists to remove.

v0.3.0 closed the case where the delivery queue discarded the tail. This closes
the one it named as remaining: the stdio read ends were closed while the readers
could still be draining them, discarding whatever the kernel still held.

The close was racing a reader that was about to finish anyway — the reap
immediately before it produces the EOF the reader stops at, because the host
closes its copy of each pipe write end at spawn. Both paths that end an instance
now wait for the readers before closing what they read.

The ordinary case costs nothing: the readers have already finished by the time
the close is reached. The wait is bounded, because a descendant that escaped the
process-group kill can hold a write end open forever and that pipe never reaches
EOF — losing a tail beats never completing a teardown.

lifecycle.Process.Kill no longer closes stdio (internal API): only its caller
knows whether anything is still reading, and it returns the moment the child is
reaped, which is precisely when a reader still has work left.

Compatibility

No public API change. A consumer on v0.3.0 upgrades by bumping the version.

The workaround v0.3.0 documented for this gap — configuring a PluginSpec.Stdio
sink and logging from there as well — is no longer needed for it, though it
remains useful for observing live output.

Validation

make ci green: lint (plain and failpoint-tagged), vet, full -race suite,
ringhook/eventhook/failpoint tag builds, allocation gate, integration, soak, and
the separate benchmark module.

The window reproduced about once in two hundred runs before this change; 400
iterations of the affected tests now pass with zero failures. A deterministic
regression test pins the ordering itself: build-tag-gated seams park the stderr
reader before it reads a byte and release it from the drain's own entry, so the
reader is provably still holding unread output when teardown begins. The seams
are compile-time eliminated in normal builds.