From 72f43c9e5959d88bb6030e9814918dd0eb31bc12 Mon Sep 17 00:00:00 2001 From: Ashime Sho Date: Wed, 6 Nov 2024 09:17:37 -0500 Subject: [PATCH] Finalize setStatus and finish setPause --- src/compiler/jsexecute.js | 10 +++++----- src/engine/block-utility.js | 4 ++-- src/engine/execute.js | 10 +++++----- src/engine/runtime.js | 30 +++++++++++++++++++++++++++--- src/engine/sequencer.js | 14 +++++++------- src/engine/thread.js | 21 +++++++++++++++++++-- 6 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js index e558b85d9c4..cce4c10d168 100644 --- a/src/compiler/jsexecute.js +++ b/src/compiler/jsexecute.js @@ -77,7 +77,7 @@ runtimeFunctions.waitThreads = `const waitThreads = function*(threads) { } } if (allWaiting) { - thread.status = 3; // STATUS_YIELD_TICK + thread.setStatus(3); // STATUS_YIELD_TICK } yield; @@ -113,16 +113,16 @@ const waitPromise = function*(promise) { // enter STATUS_PROMISE_WAIT and yield // this will stop script execution until the promise handlers reset the thread status // because promise handlers might execute immediately, configure thread.status here - thread.status = 1; // STATUS_PROMISE_WAIT + thread.setStatus(1); // STATUS_PROMISE_WAIT promise .then(value => { returnValue = value; - thread.status = 0; // STATUS_RUNNING + thread.setStatus(0); // STATUS_RUNNING }, error => { globalState.log.warn('Promise rejected in compiled script:', error); returnValue = '' + error; - thread.status = 0; // STATUS_RUNNING + thread.setStatus(0); // STATUS_RUNNING }); yield; @@ -177,7 +177,7 @@ const executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, use ) { // Yielded threads will run next iteration. if (thread.status === 2 /* STATUS_YIELD */) { - thread.status = 0; // STATUS_RUNNING + thread.setStatus(0); // STATUS_RUNNING // Yield back to the event loop when stuck or not in warp mode. if (!isWarp || isStuck()) { yield; diff --git a/src/engine/block-utility.js b/src/engine/block-utility.js index bb873d6da78..595ec7fe86c 100644 --- a/src/engine/block-utility.js +++ b/src/engine/block-utility.js @@ -105,14 +105,14 @@ class BlockUtility { * Set the thread to yield. */ yield () { - this.thread.status = Thread.STATUS_YIELD; + this.thread.setStatus(Thread.STATUS_YIELD); } /** * Set the thread to yield until the next tick of the runtime. */ yieldTick () { - this.thread.status = Thread.STATUS_YIELD_TICK; + this.thread.setStatus(Thread.STATUS_YIELD_TICK); } /** diff --git a/src/engine/execute.js b/src/engine/execute.js index 1c9e7e72a00..8750d7a3c58 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -61,7 +61,7 @@ const handleReport = function (resolvedValue, sequencer, thread, blockCached, la if (isHat) { // Hat predicate was evaluated. if (thread.stackClick) { - thread.status = Thread.STATUS_RUNNING; + thread.setStatus(Thread.STATUS_RUNNING); } else if (sequencer.runtime.getIsEdgeActivatedHat(opcode)) { // If this is an edge-activated hat, only proceed if the value is // true and used to be false, or the stack was activated explicitly @@ -74,13 +74,13 @@ const handleReport = function (resolvedValue, sequencer, thread, blockCached, la const edgeWasActivated = hasOldEdgeValue ? (!oldEdgeValue && resolvedValue) : resolvedValue; if (edgeWasActivated) { - thread.status = Thread.STATUS_RUNNING; + thread.setStatus(Thread.STATUS_RUNNING); } else { sequencer.retireThread(thread); } } else if (resolvedValue) { // Predicate returned true: allow the script to run. - thread.status = Thread.STATUS_RUNNING; + thread.setStatus(Thread.STATUS_RUNNING); } else { // Predicate returned false: do not allow script to run sequencer.retireThread(thread); @@ -108,7 +108,7 @@ const handleReport = function (resolvedValue, sequencer, thread, blockCached, la } } // Finished any yields. - thread.status = Thread.STATUS_RUNNING; + thread.setStatus(Thread.STATUS_RUNNING); } }; @@ -144,7 +144,7 @@ const handlePromiseResolution = (resolvedValue, sequencer, thread, blockCached, const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached, lastOperation) => { if (thread.status === Thread.STATUS_RUNNING) { // Primitive returned a promise; automatically yield thread. - thread.status = Thread.STATUS_PROMISE_WAIT; + thread.setStatus(Thread.STATUS_PROMISE_WAIT); } // Promise handlers primitiveReportedValue.then(resolvedValue => { diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 0a9cb9233f9..7e5c78a7584 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -2253,7 +2253,10 @@ class Runtime extends EventEmitter { // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- - // this is a WIP so it should be ignored + /** + * Set the "paused" status of the current project. + * @param {boolean} status The pause status of the project. + */ setPause(status) { status = status || false; const didChange = this.paused !== status; @@ -2263,8 +2266,15 @@ class Runtime extends EventEmitter { this.ioDevices.clock.pause(); } this.audioEngine.audioContext.suspend(); - } - if (!status && didChange) { + for (let i = this.threads.length - 1; i > -1; i--) { + if (this.threads[i].status === 5 /* STATUS_PAUSED */) continue; + this._pauseThread(this.threads[i]); + } + } else if (didChange) { + for (let i = this.threads.length - 1; i > -1; i--) { + if (this.threads[i].status !== 5 /* STATUS_PAUSED */) continue; + this._pauseThread(this.threads[i]); + } this.audioEngine.audioContext.resume(); this.ioDevices.clock.resume(); } @@ -2367,6 +2377,20 @@ class Runtime extends EventEmitter { return thread; } + /** + * Pause a thread, this toggles the pause status! + * PLEASE check the status before you use this! + * @param {!Thread} thread Thread object to restart. + */ + _pauseThread(thread) { + if (thread.status === 5 /* STATUS_PAUSED */) { + thread.setStatus(thread.previousStatus); + } else { + thread.setStatus(5); // STATUS_PAUSED + } + } + _ + emitCompileError (target, error) { this.emit(Runtime.COMPILE_ERROR, target, error); } diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js index 635b39b8913..c23791074ac 100644 --- a/src/engine/sequencer.js +++ b/src/engine/sequencer.js @@ -117,7 +117,7 @@ class Sequencer { if (activeThread.status === Thread.STATUS_YIELD_TICK && !ranFirstTick) { // Clear single-tick yield from the last call of `stepThreads`. - activeThread.status = Thread.STATUS_RUNNING; + activeThread.setStatus(Thread.STATUS_RUNNING); } if (activeThread.status === Thread.STATUS_RUNNING || activeThread.status === Thread.STATUS_YIELD) { @@ -192,7 +192,7 @@ class Sequencer { // Did the null follow a hat block? if (thread.stack.length === 0) { - thread.status = Thread.STATUS_DONE; + thread.setStatus(Thread.STATUS_DONE); return; } } @@ -223,7 +223,7 @@ class Sequencer { // If the thread has yielded or is waiting, yield to other threads. if (thread.status === Thread.STATUS_YIELD) { // Mark as running for next iteration. - thread.status = Thread.STATUS_RUNNING; + thread.setStatus(Thread.STATUS_RUNNING); // In warp mode, yielded blocks are re-executed immediately. if (isWarpMode && thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) { @@ -252,7 +252,7 @@ class Sequencer { if (thread.stack.length === 0) { // No more stack to run! - thread.status = Thread.STATUS_DONE; + thread.setStatus(Thread.STATUS_DONE); return; } @@ -332,7 +332,7 @@ class Sequencer { // In known warp-mode threads, only yield when time is up. if (thread.peekStackFrame().warpMode && thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) { - thread.status = Thread.STATUS_YIELD; + thread.setStatus(Thread.STATUS_YIELD); } else { // Look for warp-mode flag on definition, and set the thread // to warp-mode if needed. @@ -352,7 +352,7 @@ class Sequencer { thread.peekStackFrame().warpMode = true; } else if (isRecursive) { // In normal-mode threads, yield any time we have a recursive call. - thread.status = Thread.STATUS_YIELD; + thread.setStatus(Thread.STATUS_YIELD); } } } @@ -365,7 +365,7 @@ class Sequencer { thread.stack = []; thread.stackFrame = []; thread.requestScriptGlowInFrame = false; - thread.status = Thread.STATUS_DONE; + thread.setStatus(Thread.STATUS_DONE); if (thread.isCompiled) { thread.procedures = null; thread.generator = null; diff --git a/src/engine/thread.js b/src/engine/thread.js index 383873c2445..b4b940282ab 100644 --- a/src/engine/thread.js +++ b/src/engine/thread.js @@ -168,6 +168,14 @@ class Thread { */ this.stackFrames = []; + + /** + * The previous status of the thread + * Used by the pause thread feature + * @type {number} + */ + this.previousStatus = 0; /* Thread.STATUS_RUNNING */ + /** * Status of the thread, one of three states (below) * @type {number} @@ -287,6 +295,15 @@ class Thread { return 5; // used by compiler } + /** + * Sets the thread status + */ + setStatus(newStatus) { + this.previousStatus = this.status; + this.status = newStatus; + // todo: Possibly make an event? + } + /** * @param {Target} target The target running the thread. * @param {string} topBlock ID of the thread's top block. @@ -364,7 +381,7 @@ class Thread { if (this.stack.length === 0) { // Clean up! this.requestScriptGlowInFrame = false; - this.status = Thread.STATUS_DONE; + this.setStatus(Thread.STATUS_DONE); } } @@ -474,7 +491,7 @@ class Thread { } // "run util.yield", and restart the loop block - this.status = Thread.STATUS_YIELD; + this.setStatus(Thread.STATUS_YIELD); } // end of borrowed code