Name what each of the three request paths skips
Test / test (22) (pull_request) Successful in 32s
Test / test (24) (pull_request) Successful in 32s
Mirror / push (push) Has been cancelled
Test / test (26) (pull_request) Successful in 32s
Test / lint (pull_request) Successful in 23s
Test / test (18) (pull_request) Successful in 32s
Test / test (20) (pull_request) Successful in 32s

This commit is contained in:
2026-09-28 10:25:08 +02:00
parent 469a91b9bb
commit b4bb531200
4 changed files with 13 additions and 14 deletions
+1 -1
View File
@@ -772,7 +772,7 @@ rule and an index of the titles below.
`OutgoingRequests.canCarry()` reads it rather than `closed`. The gate is told what happened and `OutgoingRequests.canCarry()` reads it rather than `closed`. The gate is told what happened and
never reads back into the session: a collaborator that has to ask does not own its decision, which never reads back into the session: a collaborator that has to ask does not own its decision, which
is how the first cut ended up answering the same question two different ways at admit and at is how the first cut ended up answering the same question two different ways at admit and at
release. The retry in `carry()` asks `gate.awaitsNextLink()` rather than `canCarry()`, which also release. The retry in `requestPastDrain()` asks `gate.awaitsNextLink()` rather than `canCarry()`, which also
reads the socket: a loop condition the gate does not gate on spins against a gate that admits it reads the socket: a loop condition the gate does not gate on spins against a gate that admits it
straight back. straight back.
+10 -7
View File
@@ -81,7 +81,7 @@ export class OutgoingRequests {
this.pending.settle(seqNr, { err }); this.pending.settle(seqNr, { err });
} }
/** Sends a request and resolves with the peer's response. */ /** Sends a request once the gate has a link and the window a slot, and resolves with the peer's response. */
request(input: PduObjectInput, options: SendOptions): Promise<Result<{ pduObj: PduObject }>> { request(input: PduObjectInput, options: SendOptions): Promise<Result<{ pduObj: PduObject }>> {
// Ahead of the drain, so a misuse is named as one rather than blamed on the shutdown. // Ahead of the drain, so a misuse is named as one rather than blamed on the shutdown.
const wrong = misuse(input); const wrong = misuse(input);
@@ -93,11 +93,11 @@ export class OutgoingRequests {
return Promise.resolve({ err: new Error('Session is shutting down') }); return Promise.resolve({ err: new Error('Session is shutting down') });
} }
return this.carry(input, options); return this.requestPastDrain(input, options);
} }
/** The same path without the drain's refusal, which a receipt for a held message has to take. */ /** request() without the drain's refusal, which a receipt for a held message has to take. */
async carry( async requestPastDrain(
input: PduObjectInput, input: PduObjectInput,
options: SendOptions, options: SendOptions,
): Promise<Result<{ pduObj: PduObject }>> { ): Promise<Result<{ pduObj: PduObject }>> {
@@ -109,7 +109,7 @@ export class OutgoingRequests {
if (bindCommands.includes(input.cmdName)) { if (bindCommands.includes(input.cmdName)) {
const shut = this.gate.refusal(); const shut = this.gate.refusal();
return shut ? { err: shut } : this.now(input, options); return shut ? { err: shut } : this.requestPastDrainGateAndWindow(input, options);
} }
const waitForLink = this.gate.hold(options.signal); const waitForLink = this.gate.hold(options.signal);
@@ -129,8 +129,11 @@ export class OutgoingRequests {
} }
} }
/** Past the gate, the window and a drain, for what has to go out either way. */ /** Straight onto the current link, for what has to go out either way. */
async now(input: PduObjectInput, options: SendOptions = {}): Promise<Result<{ pduObj: PduObject }>> { async requestPastDrainGateAndWindow(
input: PduObjectInput,
options: SendOptions = {},
): Promise<Result<{ pduObj: PduObject }>> {
return (await this.attempt(input, options)).result; return (await this.attempt(input, options)).result;
} }
+2 -3
View File
@@ -235,9 +235,8 @@ export class Session extends EventEmitter<SessionEvents> {
async unbind(): Promise<VoidResult> { async unbind(): Promise<VoidResult> {
const drained = await this.drain(undefined); const drained = await this.drain(undefined);
const wasOpen = this.lifecycle === 'attached'; const wasOpen = this.lifecycle === 'attached';
// now(), not send(): a drain refuses a send, and the unbind goes out either way.
const sent = wasOpen const sent = wasOpen
? await this.outgoing.now({ cmdName: 'unbind' }) ? await this.outgoing.requestPastDrainGateAndWindow({ cmdName: 'unbind' })
: { err: new Error('Session is closed') }; : { err: new Error('Session is closed') };
const closedOnUnbind = wasOpen && this.lifecycle !== 'attached'; const closedOnUnbind = wasOpen && this.lifecycle !== 'attached';
@@ -274,7 +273,7 @@ export class Session extends EventEmitter<SessionEvents> {
reportError: err => { this.emit('sessionError', err); }, reportError: err => { this.emit('sessionError', err); },
reportMessageDlr: merged => { this.emit('messageDlr', merged); }, reportMessageDlr: merged => { this.emit('messageDlr', merged); },
send: input => this.send(input), send: input => this.send(input),
sendPastDrain: input => this.outgoing.carry(input, {}), sendPastDrain: input => this.outgoing.requestPastDrain(input, {}),
smsListeners: () => this.listenerCount('sms'), smsListeners: () => this.listenerCount('sms'),
}; };
} }
-3
View File
@@ -204,9 +204,6 @@ and 5. Every seat ranked the session's lifecycle hardest and least wanted to mod
- [ ] **Lift Locality to 7, and confirm it with a scoring run.** A run reading 7.0 or above also - [ ] **Lift Locality to 7, and confirm it with a scoring run.** A run reading 7.0 or above also
retires the #30 decision. The sub-items are what the 2026-09-28 run named, most seats first. retires the #30 decision. The sub-items are what the 2026-09-28 run named, most seats first.
- [ ] **Name what `request()`, `carry()` and `now()` each skip.** Three ways onto the wire differ
only in which of the drain, the gate and the window they bypass, and none of the names says
which. Three seats.
- [ ] **Give the held-message flow one place a reader can follow it.** Whether a drain still waits - [ ] **Give the held-message flow one place a reader can follow it.** Whether a drain still waits
on a message is spread over `emitSms()`, `MessageHold`, the session's rejection route and on a message is spread over `emitSms()`, `MessageHold`, the session's rejection route and
`Sms.isHeld()`. Four seats. `Sms.isHeld()`. Four seats.