Wait out the messages the application holds before shutting a session down

This commit is contained in:
2026-09-01 13:58:28 +02:00
parent a99e1227ac
commit 3bcea108e3
12 changed files with 263 additions and 79 deletions
+6 -32
View File
@@ -1,8 +1,10 @@
import { IdleWaiters } from './idle-waiters.ts';
/** Caps how many requests are on the wire at once; anything past the limit waits its turn. */
export class SendWindow {
private readonly idleWaiters = new IdleWaiters();
private readonly limit: number;
private readonly waiting: (() => void)[] = [];
private readonly waitingForIdle: (() => void)[] = [];
private inFlight = 0;
constructor(limit: number) {
@@ -32,9 +34,7 @@ export class SendWindow {
if (this.inFlight > 0) return;
for (const resolve of this.waitingForIdle.splice(0)) {
resolve();
}
this.idleWaiters.settle();
}
/** Everything the caller is still owed: on the wire, plus queued behind a full window. */
@@ -42,34 +42,8 @@ export class SendWindow {
return this.inFlight + this.waiting.length;
}
/**
* Resolves 0 once nothing is left, or with what still is when the timeout or the signal cuts the
* wait short. A timeout of 0 waits forever.
*/
/** Resolves 0 once nothing is left on the wire, or with what still is. */
idle(timeout: number, signal?: AbortSignal): Promise<number> {
if (this.inFlight === 0) return Promise.resolve(0);
if (signal?.aborted === true) return Promise.resolve(this.unfinished());
return new Promise<number>(resolve => {
let timer: NodeJS.Timeout | undefined = undefined;
const done = (): void => {
const index = this.waitingForIdle.indexOf(done);
if (timer) clearTimeout(timer);
if (index !== -1) this.waitingForIdle.splice(index, 1);
signal?.removeEventListener('abort', done);
resolve(this.unfinished());
};
if (timeout > 0) {
timer = setTimeout(done, timeout);
timer.unref();
}
signal?.addEventListener('abort', done, { once: true });
this.waitingForIdle.push(done);
});
return this.idleWaiters.wait(() => this.unfinished(), timeout, signal);
}
}