/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2023 Tony Garnock-Jones import { IdentitySet } from '@preserves/core'; import type { Actor, ExitReason } from './actor.js'; const LIMIT = 25000; export enum ActorSpaceState { RUNNING, PAUSED, TERMINATED, } export class ActorSpace { actors = new IdentitySet(); state = ActorSpaceState.RUNNING; taskCounter = 0; delayedTasks: Array<() => void> = []; taskFlushHandle: ReturnType | null = null; register(actor: Actor): boolean { if (this.state === ActorSpaceState.TERMINATED) return false; this.actors.add(actor); return true; } deregister(actor: Actor) { this.actors.delete(actor); if (this.actors.size === 0) { this.shutdown({ ok: true }); } } shutdown(reason: Exclude) { if (this.state === ActorSpaceState.TERMINATED) return; this.state = ActorSpaceState.TERMINATED; Array.from(this.actors.values()).forEach(a => a._terminateWith(reason)); } queueTask(f: () => void) { switch (this.state) { case ActorSpaceState.TERMINATED: break; case ActorSpaceState.PAUSED: this.delayedTasks.push(f); break; case ActorSpaceState.RUNNING: this.taskCounter++; if (this.taskCounter === LIMIT) { this.taskFlushHandle = setTimeout(() => this._scheduleDelayedTasks(), 0); } if (this.taskCounter >= LIMIT) { this.delayedTasks.push(f); } else { queueMicrotask(f); } break; } } _scheduleDelayedTasks() { this.taskCounter = 0; this.delayedTasks.forEach(queueMicrotask); this.delayedTasks = []; } pause(cb: () => void): boolean { switch (this.state) { case ActorSpaceState.TERMINATED: return false; case ActorSpaceState.PAUSED: queueMicrotask(cb); return true; case ActorSpaceState.RUNNING: this.state = ActorSpaceState.PAUSED; if (this.taskFlushHandle !== null) { clearTimeout(this.taskFlushHandle); this.taskFlushHandle = null; } queueMicrotask(cb); return true; } } unpause(): boolean { switch (this.state) { case ActorSpaceState.TERMINATED: return false; case ActorSpaceState.PAUSED: this.state = ActorSpaceState.RUNNING; this._scheduleDelayedTasks(); return true; case ActorSpaceState.RUNNING: return true; } } }