27 lines
1.2 KiB
TypeScript
27 lines
1.2 KiB
TypeScript
import { fault } from '../canvas-core/values.ts';
|
|
import { createDeque } from './structures.ts';
|
|
|
|
/** Shared bounded transport capacity for writes, uploads and run reads. */
|
|
export function createNetworkGate(limit = 2) {
|
|
if(!Number.isSafeInteger(limit)||limit<1)throw fault('invalid_concurrency');
|
|
let active = 0;
|
|
const waiting = createDeque<{ signal: AbortSignal; grant(): void; cancelled: boolean }>();
|
|
async function enter(signal: AbortSignal) {
|
|
signal.throwIfAborted();
|
|
if (active < limit) { active++; return; }
|
|
await new Promise<void>((resolve,reject) => {
|
|
const item = { signal, cancelled: false, grant() { signal.removeEventListener('abort',abort);resolve(); } };
|
|
const abort = () => { item.cancelled=true;signal.removeEventListener('abort',abort);reject(signal.reason); };
|
|
signal.addEventListener('abort',abort,{once:true});waiting.push(item);
|
|
});
|
|
}
|
|
function release() {
|
|
let item=waiting.pop();while(item?.cancelled)item=waiting.pop();
|
|
if(item)item.grant();else active--;
|
|
}
|
|
return Object.freeze({ async run<T>(signal: AbortSignal, callback: () => Promise<T>): Promise<T> {
|
|
await enter(signal);
|
|
try { signal.throwIfAborted();return await callback(); } finally { release(); }
|
|
} });
|
|
}
|