Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

## Unreleased

### One reader for the process table

- Process facts now come from one module. On Linux it reads `/proc` and spawns
nothing at all. On macOS `ps` remains, but it is read once per operation
rather than once per process per poll — the difference between 240 spawns
inside a 1500 ms deadline and 60.
- Every query separates three answers: the fact, "the table was read and this
process is not in it", and "I could not find out". A `ps` that is slow,
truncated or silent now produces the third rather than the second. There is
no default and no conversion that turns silence into absence by accident.
- A listing that does not contain the process that read it is treated as
truncated rather than as an empty machine. `ps` always lists at least itself.
- `recovery.processStartToken` is unchanged and still comes from
`ps -o lstart=`. Its exact text, including the two spaces before a
single-digit day, is a contract with the Rust tool through a shared registry.
The in-memory identity used by the teardown is a separate branded type so the
two cannot be compared by accident.
- Production `ps` call sites: six down to three, and none of them inside a
per-process poll loop.

### `pty kill` finishes the job

- After the daemon has gone, `pty kill` re-reads the process table. If anything
Expand Down
15 changes: 10 additions & 5 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ import {
import { snapshotDescendantProcesses } from "./process-tree.ts";
import { aftermathOf, allGone, killOutcomeLines, verifiedEmpty } from "./kill-report.ts";
import {
groupsInTree, listProcessesWithGroups, membersOfGroups, ownProcessGroup,
parseRows, signalGroup, sweepGroups,
groupsInTree, membersOfGroups, ownProcessGroup, signalGroup, sweepGroups,
} from "./process-groups.ts";
import { openSource, valueOf } from "./proc-table.ts";
import { spawnDaemon, resolveCommand } from "./spawn.ts";
import {
acquireEventLock, appendEventSyncLocked, EventFollower, EventWriter, EventType, releaseEventLock,
Expand Down Expand Up @@ -2641,7 +2641,7 @@ const ESCALATE_KILL_WAIT_MS = 1_000;
* table. Returns the pids still alive in those groups. */
async function escalateOverGroups(groups: number[]): Promise<number[]> {
return sweepGroups(groups, ownProcessGroup(), ESCALATE_TERM_WAIT_MS, ESCALATE_KILL_WAIT_MS, {
live: (targets) => membersOfGroups(targets, parseRows(listProcessesWithGroups())),
live: (targets) => membersOfGroups(targets, openSource()),
signal: signalGroup,
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
});
Expand Down Expand Up @@ -2677,7 +2677,7 @@ async function cmdKill(name: string): Promise<void> {
// Groups come from the raw listing, NOT from `before`. The snapshot drops a
// descendant whose start token cannot be read, and that process is then never
// signalled. A group needs no identity, so this reaches it anyway.
const groups = groupsInTree(session.pid, parseRows(listProcessesWithGroups()));
const groups = groupsInTree(session.pid, openSource());

try {
process.kill(session.pid, "SIGTERM");
Expand Down Expand Up @@ -2710,7 +2710,12 @@ async function cmdKill(name: string): Promise<void> {
escalated = await escalateOverGroups(groups);
// Re-measure. The report must describe the machine now, not the signals
// that were sent at it.
after = aftermathOf(before, readProcessStartToken, hasProcessExitedForReap);
const again = openSource();
after = aftermathOf(
before,
(pid) => valueOf(again.identity(pid)),
hasProcessExitedForReap,
);
}
const outcome = killOutcomeLines(name, after, escalated);
for (const line of outcome.out) console.log(line);
Expand Down
6 changes: 3 additions & 3 deletions src/kill-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ export function verifiedEmpty(after: Aftermath, escalated?: number[]): boolean {
*/
export function aftermathOf(
before: ProcessIdentity[],
readStartToken: (pid: number) => string | null,
readIdentity: (pid: number) => string | null,
exited: (pid: number) => boolean,
): Aftermath {
const after: Aftermath = { survived: [], unknown: [] };
for (const identity of before) {
if (exited(identity.pid)) continue;
const token = readStartToken(identity.pid);
if (token === identity.processStartToken) after.survived.push(identity.pid);
const token = readIdentity(identity.pid);
if (token === identity.identity) after.survived.push(identity.pid);
// A different token is a PID the kernel handed to somebody else.
else if (token === null) after.unknown.push(identity.pid);
}
Expand Down
Loading
Loading