import { makeFastTick } from 'virtjs/devices/timers/utils'; let HANDLER_FN_SIZE = 31; let HANDLER_FN_PATTERN = 0x7FFFFFFF; export class SerialTimer { /** * A SerialTimer is a synchronous timer device. You can use it to run your emulator synchronously (ie. blocking the main thread). It also has the ability to only run a finite number of ticks before returning, which is quite valuable when debugging engines. * * @constructor * @implements {Timer} */ constructor() { this.running = false; this.nested = false; this.queues = [ [ ], [ ] ]; this.activeQueueIndex = 0; } nextTick(callback) { let activeQueueIndex = this.activeQueueIndex; let queue = this.queues[activeQueueIndex]; let callbackIndex = queue.length; queue.push(callback); return activeQueueIndex << HANDLER_FN_SIZE | callbackIndex; } cancelTick(handler) { let activeQueueIndex = handler >>> HANDLER_FN_SIZE; let callbackIndex = handler & HANDLER_FN_PATTERN; this.queues[activeQueueIndex][callbackIndex] = null; } start(beginning, ending) { if (this.running) throw new Error(`You can't start a timer that is already running`); if (this.nested) throw new Error(`You can't start a timer from its callbacks - use resume instead`); let fastTick = makeFastTick(beginning, ending, () => { this.one(); }); this.running = true; this.nested = true; while (this.running) fastTick(); this.nested = false; } resume() { if (!this.nested) throw new Error(`You can't resume a timer from anywhere else than its callbacks - use start instead`); if (this.running) return; this.running = true; } stop() { if (!this.running) return; this.running = false; } /** * Start the emulator. Run a single cycle then exit. */ one() { let activeQueueIndex = this.activeQueueIndex; this.activeQueueIndex = activeQueueIndex ^ 1; let queue = this.queues[activeQueueIndex]; for (let t = 0, T = queue.length; t < T; ++t) queue[t] && queue[t](); queue.length = 0; } }