Repository Public
1/**2 * Readers/writer gate of one run context. Steps hold it shared while they execute; a suspension3 * (durable wait, timer, reconcile question) takes it exclusively, so a run is never put to sleep —4 * and its lease never released — while one of its steps is still executing or committing.5 * Writers have priority; nothing here is re-entrant (exec must not call ctx.* itself).6 */7export class Gate {8 private readers = 0;9 private writer = false;10 private writersWaiting = 0;11 private parked: (() => void)[] = [];1213 private park(): Promise<void> {14 return new Promise((resolve) => this.parked.push(resolve));15 }16 private wakeAll(): void {17 const p = this.parked;18 this.parked = [];19 for (const r of p) r();20 }21 async shared(): Promise<void> {22 while (this.writer || this.writersWaiting > 0) await this.park();23 this.readers++;24 }25 releaseShared(): void {26 this.readers--;27 this.wakeAll();28 }29 async exclusive(): Promise<void> {30 this.writersWaiting++;31 try {32 while (this.writer || this.readers > 0) await this.park();33 } finally {34 this.writersWaiting--;35 }36 this.writer = true;37 }38 releaseExclusive(): void {39 this.writer = false;40 this.wakeAll();41 }42 /** Resolves when no step holds the gate. */43 async idle(): Promise<void> {44 while (this.writer || this.readers > 0 || this.writersWaiting > 0) await this.park();45 }46}47