An agy sign-in completes once its login has stored its credential: the runner closes the login's terminal before it kills and reaps it #192

Merged
agra merged 1 commit from task/3936-an-agy-sign-in-hangs-after-its-login-suc into master 2026-10-30 14:25:53 +02:00
Owner

Tracker: #3936

What it delivers

A sign-in whose login runs on a terminal now closes that terminal before it asks the runner's main thread to kill and reap the login (terminal_login, runner/sign_in.sx). An agy sign-in then completes with its models once the home's keychain holds the login, and the runner's main thread no longer blocks in its reap.

The cause

A login that has opened its terminal as its controlling one, as agy does, cannot finish exiting while output it wrote there is still unread. XNU's proc_exit drains a session leader's controlling terminal with ttywait at t_timeout 0, which has no bound. The bounded drain of a slave's last close (ptsclose, 60 ticks) never runs here, because opening /dev/tty holds an extra reference on the terminal until the leader's exit.

Before this change, the pump thread stopped reading the terminal once the home's login script found the login. It then asked for parent.kill() while it still held the master, and the main thread's wait(true) waited on that exit for good.

Checked on s32 against the real agy 1.4.11, run on a pty with a scratch HOME and its menu shown. agy holds the terminal on fds 0, 5 and 6. Killed with a redraw left unread, it stayed ?Es for more than 10 s while the master was open, and exited the moment the master closed. Killed with the master closed first, its reap returned in 0.06 s.

Regression

tests/stub_sign_in.sh's agy login, once signed in, now stays up redrawing its screen without pause on /dev/tty, as agy's interface does. tests/sign_in.sx check_agy checks that the sign-in is done ("the agy sign-in is done once the home's keychain holds a login, though its login stays up redrawing its terminal"). If the sign-in still waits after WAIT_MS, the test prints its failure and ends, removing its sandbox and the login's home. It cannot stop a channel whose main thread is held, and this way it does not hang.

  • Fail-before. The head's two test files over master 5c98cb54's runner code, from a git archive arm, give FAIL the agy sign-in is done … and SIGN IN FAIL (1 checks), exit 1, about 33 s, in 4 of 4 runs. sample of the test process while it waited matched the field report:
    • main thread: Channel.serve → __wait4;
    • the sign-in's pump thread: terminal_login → Parent.kill → Slot.call → _pthread_cond_wait;
    • the stub login: ?Es.
  • Pass-after. At the head the same file gives SIGN IN OK (101 ok) in about 7 s, in 4 of 4 runs. No stub process and no /tmp/sak-sign-in-* home was left after either arm.

Gates at the head

  • full, tracker gate run 948 (this run's gate #1), passed at e32207a5:
    • tests/run.sh: 69 of 69 test files, sign_in in 7 s;
    • release.sh and step9;
    • the deploy contract;
    • the main Playwright config: 777 passed, 1 skipped;
    • the runner config: 10 passed.

The change touches nothing under web/, so it declares no golden set.

Tracker: [#3936](https://sak.swipelab.com/tasks/3936) ## What it delivers A sign-in whose login runs on a terminal now closes that terminal before it asks the runner's main thread to kill and reap the login (`terminal_login`, `runner/sign_in.sx`). An agy sign-in then completes with its models once the home's keychain holds the login, and the runner's main thread no longer blocks in its reap. ## The cause A login that has opened its terminal as its controlling one, as agy does, cannot finish exiting while output it wrote there is still unread. XNU's `proc_exit` drains a session leader's controlling terminal with `ttywait` at `t_timeout` 0, which has no bound. The bounded drain of a slave's last close (`ptsclose`, 60 ticks) never runs here, because opening `/dev/tty` holds an extra reference on the terminal until the leader's exit. Before this change, the pump thread stopped reading the terminal once the home's login script found the login. It then asked for `parent.kill()` while it still held the master, and the main thread's `wait(true)` waited on that exit for good. Checked on s32 against the real agy 1.4.11, run on a pty with a scratch HOME and its menu shown. agy holds the terminal on fds 0, 5 and 6. Killed with a redraw left unread, it stayed `?Es` for more than 10 s while the master was open, and exited the moment the master closed. Killed with the master closed first, its reap returned in 0.06 s. ## Regression `tests/stub_sign_in.sh`'s agy login, once signed in, now stays up redrawing its screen without pause on `/dev/tty`, as agy's interface does. `tests/sign_in.sx` `check_agy` checks that the sign-in is done ("the agy sign-in is done once the home's keychain holds a login, though its login stays up redrawing its terminal"). If the sign-in still waits after `WAIT_MS`, the test prints its failure and ends, removing its sandbox and the login's home. It cannot stop a channel whose main thread is held, and this way it does not hang. - **Fail-before.** The head's two test files over master 5c98cb54's runner code, from a `git archive` arm, give `FAIL the agy sign-in is done …` and `SIGN IN FAIL (1 checks)`, exit 1, about 33 s, in 4 of 4 runs. `sample` of the test process while it waited matched the field report: - main thread: `Channel.serve → __wait4`; - the sign-in's pump thread: `terminal_login → Parent.kill → Slot.call → _pthread_cond_wait`; - the stub login: `?Es`. - **Pass-after.** At the head the same file gives `SIGN IN OK` (101 ok) in about 7 s, in 4 of 4 runs. No stub process and no `/tmp/sak-sign-in-*` home was left after either arm. ## Gates at the head - `full`, tracker gate run 948 (this run's gate #1), passed at e32207a5: - tests/run.sh: 69 of 69 test files, sign_in in 7 s; - release.sh and step9; - the deploy contract; - the main Playwright config: 777 passed, 1 skipped; - the runner config: 10 passed. The change touches nothing under `web/`, so it declares no golden set.
A login that opened its terminal as its controlling one, as agy does,
stays in its exit while output it left on the terminal is unread. The
sign-in's pump thread asked the main thread to kill and reap the login
while it still held the terminal and no longer read it, so the reap
blocked the main thread for good and the sign-in never completed.
agra changed title from An agy sign-in completes once its login has stored its credential: the runner ends the login's terminal before it waits on the killed login to An agy sign-in completes once its login has stored its credential: the runner closes the login's terminal before it kills and reaps it 2026-10-30 14:04:55 +02:00
agra merged commit 8c2ce26eff into master 2026-10-30 14:25:53 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sx/sak!192
No description provided.