function noop() {} export interface Deferred extends Promise { resolve(value: T | PromiseLike): void; reject(reason?: unknown): void; } export function deferred() { const { promise, resolve, reject } = Promise.withResolvers(); const p = promise as Deferred; return (p.resolve = resolve), (p.reject = reject), p; } export function notifier() { let p: Deferred | null = null; return { listen(): Promise { return (p ??= deferred()); }, notify(value: T) { p &&= (p.resolve(value), null); }, }; } export interface Channel { send: Sender; recv: Receiver; } export interface Sender { (value: T): void; close(reason?: unknown): void; } export interface Receiver { (): Promise | T | null; try(): T | null; close(reason?: unknown): void; } export function channel(): Channel { const q = new Map(); let head = 0; let tail = 0; let open = true; let err: unknown = null; type Entry = | { has: true; value: T } | { has: false; value: Deferred }; function send(value: T) { if (open) { const i = head++; let n = q.get(i); if (!n) q.set(i, (n = { has: true, value })); else if (!n.has) q.delete(i), n.value.resolve(value); else n.value = value; } else { if (err !== null) throw err; else return; } } function recv() { if (open) { const i = tail++; let n = q.get(i); if (!n) return q.set(i, (n = { has: false, value: deferred() })), n.value; else if (!n.has) return n.value; else return q.delete(i), n.value; } else { if (err !== null) throw err; else return null; } } function try_recv() { if (open) { const n = q.get(tail); if (n?.has) return q.delete(tail++), n.value; else return null; } else { return null; } } recv.try = try_recv; send.close = recv.close = function close(reason?: unknown) { if (!open) return; else (open = false), (err = reason ?? null); for (const p of q.values()) { if (p.has) continue; else if (err !== null) p.value.reject(err); else p.value.resolve(null); } q.clear(); }; return { send, recv }; } channel.sender = function sender( f: (recv: Receiver) => void | PromiseLike ): Sender { const { send, recv } = channel(); Promise.resolve(f(recv)).then(noop).then(recv.close, recv.close); return send; }; channel.receiver = function receiver( f: (send: Sender) => void | PromiseLike ): Receiver { const { send, recv } = channel(); Promise.resolve(f(send)).then(noop).then(send.close, send.close); return recv; }; export function semaphore(count = 1) { const { send: signal, recv: wait } = channel(); let n = count; async function acquire() { if (--n < 0) await wait(); return release; } function release() { if (n++ < 0) signal(); } function reset(new_count = count) { n = count = new_count; } acquire.release = release; acquire.reset = reset; release[Symbol.dispose] = release; release satisfies Disposable; return acquire; } export interface PoolToken extends Iterable { readonly value: T; release(): void; } export function pool(size: number, f: () => T | PromiseLike) { const lock = semaphore(size); const all = new Set(); const free: T[] = []; const Token = class implements PoolToken { #value; #open = true; get value() { if (this.#open) return this.#value; else throw new TypeError(`borrowed value is already released`); } constructor(value: T) { this.#value = value; } *[Symbol.iterator]() { yield this.value; } release() { this.#open &&= (lock.release(), all.has(this.#value) && free.push(this.#value), false); } [Symbol.dispose]() { this.release(); } }; async function acquire(): Promise> { await lock(); try { let value; if (free.length !== 0) value = free.pop() as T; else value = await f(); return all.add(value), new Token(value); } catch (e) { throw (lock.release(), e); } } function forget(value: T) { if (all.delete(value)) { const idx = free.indexOf(value); if (idx !== -1) free.splice(idx, 1); } } acquire.forget = forget; return acquire; }