From 2152b995d437d88eb94ba7120fe1f10f05f8a8f2 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Mon, 28 Sep 2026 21:27:00 +0200 Subject: [PATCH] Name the loop's own flag, and drop droppedWhileDraining() --- docs/decisions.md | 2 ++ src/link-life.ts | 6 +++--- src/outgoing-requests.ts | 7 +------ src/session.ts | 3 ++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/decisions.md b/docs/decisions.md index 4988b42..4dc690b 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -768,6 +768,8 @@ rule and an index of the titles below. is how the first cut ended up answering the same question two different ways at admit and at release. Every other collaborator reads whether the link lives from it and keeps no copy: five copies held in step by statement order were what the 2026-09-28 comprehension runs ranked hardest. + `ReconnectLoop.halted` is the loop's own, for its timer, because `client()` also runs a loop with + no session behind it for `fromStart`; a session's loop is stopped by `Session.stop()` alone. The retry in `requestPastDrain()` asks `link.awaitsNextLink()` rather than `canCarry()`, which also reads the socket: a loop condition the link does not gate on spins against a link that admits it straight back. diff --git a/src/link-life.ts b/src/link-life.ts index 0408b0e..558fee4 100644 --- a/src/link-life.ts +++ b/src/link-life.ts @@ -59,9 +59,9 @@ export class LinkLife { return this.phase === 'ended'; } - /** False once the session is shutting down: nothing new is taken, and no link follows this one. */ - isAccepting(): boolean { - return !this.stopped; + /** The session is shutting down: nothing new is taken, and no link follows this one. */ + isStopped(): boolean { + return this.stopped; } /** Whether a link that drops now is followed by another. */ diff --git a/src/outgoing-requests.ts b/src/outgoing-requests.ts index 233eced..fc09eb0 100644 --- a/src/outgoing-requests.ts +++ b/src/outgoing-requests.ts @@ -54,11 +54,6 @@ export class OutgoingRequests { return this.link.isUp() && !this.transport.sock.destroyed; } - /** The link went before the drain finished, so an empty window says nothing about the peer. */ - droppedWhileDraining(): boolean { - return !this.link.isAccepting() && !this.canCarry(); - } - /** The link is gone, and every answer still owed on it with it. */ linkLost(): void { this.pending.settleAll(new Error('Session closed before a response arrived')); @@ -82,7 +77,7 @@ export class OutgoingRequests { if (wrong) return Promise.resolve({ err: wrong }); // With no link, the request is refused as closed further on. - if (!this.link.isAccepting() && this.canCarry()) { + if (this.link.isStopped() && this.canCarry()) { return Promise.resolve({ err: new Error('Session is shutting down') }); } diff --git a/src/session.ts b/src/session.ts index 76d0856..1969026 100644 --- a/src/session.ts +++ b/src/session.ts @@ -333,7 +333,8 @@ export class Session extends EventEmitter { const messages = await this.incoming.drain(this.answering(timeout), signal); const requests = await this.outgoing.drain(leftOf(deadline), signal); - if (this.outgoing.droppedWhileDraining()) { + // The link went before the drain finished, so an empty window says nothing about the peer. + if (!this.outgoing.canCarry()) { return { err: new Error('The session closed before the drain finished') }; }