Skip to content

Bound the pty hangup reads with an alarm - #352

Open
alanhc wants to merge 1 commit into
sysprog21:mainfrom
alanhc:test-pty-hangup-alarm
Open

Bound the pty hangup reads with an alarm#352
alanhc wants to merge 1 commit into
sysprog21:mainfrom
alanhc:test-pty-hangup-alarm

Conversation

@alanhc

@alanhc alanhc commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Closes #275.

The hangup checks in tests/test-pty.c read from a master whose slaves have
all closed. Linux answers EIO there, but on a tree without the support nothing
fails the read at all -- elfuse's keepalive slave holds the host pty open, so
the host has no reason to. The read parks, and a run against such a tree stalls
instead of reporting.

tests/test-devpts.c already takes the position that a test guarding a fix has
to fail when the fix is absent, and the readv case at the bottom of this file
does too: signal(SIGALRM, ...) plus alarm(10) around the blocking call,
cleared with alarm(0) once it returns. The first hangup block never got the
same treatment. This applies it there.

Both reads sit in one window. The second is the wedge; the drain above it reads
the same hung-up master with no deadline of its own. Hoisting the second read
out of EXPECT_TRUE is what lets the alarm be cleared before the verdict is
printed, as the readv case does.

Two smaller things came out of measuring it. The handler was silent, and its
_exit(2) discarded whatever stdout still held, so a wedged run through a pipe
produced no output at all -- neither the results collected so far nor a word
about why it stopped. It now names the timeout on stderr, and stdout is
line-buffered, so the log ends with every check that did complete followed by
the reason the run went no further.

Evidence

Measured against a tree with proc_pty_master_hung_up() stubbed to return false, which is what "without the support" means here.

before after
outcome still running at a 60s cap, killed exit 2 at 10s
output nothing 37 lines of results, master reports POLLHUP once the slave closes FAIL, then the timeout line

After, on the stubbed tree:

  packet-mode read carries a status byte OK
  master reports POLLHUP once the slave closes FAIL: no POLLHUP after the last slave closed (errno=2)
  queued output survives the hangup OK

test-pty: TIMEOUT waiting on a hung-up master read

On an unmodified tree the file still reports 70 passed, 0 failed - PASS,
exit 0, in 3 seconds.

make check-format passes (clang-format 22.1.8, commentflow), make indent
leaves the file unchanged, and the cross build is clean under -Wall -Wextra.
A full make check did not run locally for want of aarch64-none-elf-as, so
everything outside test-pty is on CI.


Summary by cubic

Fixes #275 by bounding the hung-up master reads in tests/test-pty.c with an alarm: on trees without hangup support those reads previously blocked forever, now the run exits 2 at 10 seconds with a timeout message.

Bug Fixes

  • Applies the existing SIGALRM + alarm(10) pattern from the readv case, clearing the alarm before printing the verdict.
  • Writes the timeout reason to stdout, not stderr (test-matrix.sh discards stderr), and line-buffers stdout so the log retains all completed checks when the alarm fires.

Written for commit 2848eb0. Summary will update on new commits.

Review in cubic

@alanhc
alanhc force-pushed the test-pty-hangup-alarm branch from 60785bf to 6ac7308 Compare September 1, 2026 19:50
cubic-dev-ai[bot]

This comment was marked as resolved.

The hangup checks in tests/test-pty.c read from a master whose slaves
have all closed. Linux answers EIO there, but on a tree without the
support nothing fails the read at all: elfuse's keepalive slave holds
the host pty open, so the host has no reason to. The read parks, and a
run against such a tree stalls instead of reporting. A control run
took ten minutes and had to be killed.

A test that guards a fix has to fail when the fix is absent.
tests/test-devpts.c already takes that position for its pty round
trip, and the readv case at the bottom of this file does too, with
signal(SIGALRM, ...) plus alarm(10) around the blocking call and
alarm(0) once it returns. The first hangup block never got the same
treatment.

It gets it now. Both reads sit in one window: the second is the wedge,
and the drain above it reads the same hung-up master with no deadline
of its own. Hoisting the second read out of EXPECT_TRUE is what lets
the alarm be cleared before the verdict is printed, as the readv case
does.

The handler was silent, and its _exit(2) discarded whatever stdout
still held, so a wedged run through a pipe produced no output at all
-- neither the results collected so far nor a word about why it
stopped. It now names the timeout, and stdout is line-buffered, so the
log ends with every check that did complete followed by the reason the
run went no further. The message goes to stdout rather than stderr
because test-matrix.sh's run_elfuse discards stderr, and that lane is
the one most likely to meet a timeout with nobody watching.

Measured against a tree with proc_pty_master_hung_up stubbed to false,
which is what "without the support" means here. Before: the run sat
past a 60 second cap having printed nothing. After: the POLLHUP check
fails, the drain passes, the EIO read trips the alarm at ten seconds,
and the process exits 2 with 37 lines of results behind it. An
unmodified tree still reports 70 passed, 0 failed.
@alanhc
alanhc force-pushed the test-pty-hangup-alarm branch from 6ac7308 to 2848eb0 Compare September 1, 2026 20:18

@jserv jserv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One note outside the diff: the description says "It now names the timeout on stderr", but the handler writes STDOUT_FILENO and the new comment argues at length for stdout. One word to fix in the prose.

Comment thread tests/test-pty.c
* rather than in one of its own.
*/
signal(SIGALRM, hup_on_alarm);
alarm(10);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The alarm stays armed while the drain verdict prints. If the drain read burns most of the window, SIGALRM can fire after that read already returned, and it can land between TEST()'s label (printed without a newline, so still buffered) and the newline EXPECT_TRUE adds. The handler comment claims both guarded reads sit at a line boundary; that holds for the reads, not for the gap between them, and the TIMEOUT line would then name a read that did not block. Clearing at line 850 and re-arming just before the EIO read costs two lines.

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.

Pty hangup checks in test-pty block instead of failing

2 participants