Add pause-resume-stop functionality and UI

This commit is contained in:
Nilay Majorwar
2021-12-15 21:31:25 +05:30
parent 29b243d6f2
commit 13ff6da638
8 changed files with 262 additions and 31 deletions

View File

@@ -14,6 +14,8 @@ class ExecutionController<RS> {
private _engine: LanguageEngine<RS>;
private _breakpoints: number[] = [];
private _result: StepExecutionResult<RS> | null;
private _resolvePause: (() => void) | null = null;
private _isPaused: boolean = false;
/**
* Create a new ExecutionController.
@@ -51,16 +53,75 @@ class ExecutionController<RS> {
this._breakpoints = points;
}
/**
* Pause the ongoing execution.
* - If already paused, returns immediately
* - Queues up with any existing pause calls
* @returns Promise that resolves when execution is paused
*/
async pauseExecution(): Promise<void> {
// If already paused, return immediately
if (this._isPaused) return;
// If there's another "pause" call waiting, chain up with older resolver.
// This kinda creates a linked list of resolvers, with latest resolver at head.
if (this._resolvePause) {
console.log("Chaining pause calls");
return new Promise((resolve) => {
// Keep a reference to the existing resolver
const oldResolve = this._resolvePause;
// Replace resolver with new chained resolver
this._resolvePause = () => {
oldResolve && oldResolve();
resolve();
};
});
}
// Else, create a callback to be called by the execution loop
// when it finishes current execution step.
return new Promise(
(resolve) =>
(this._resolvePause = () => {
this._resolvePause = null;
this._isPaused = true;
resolve();
})
);
}
/**
* Execute the loaded program until stopped.
* @param param0.interval Interval between two execution steps
* @param param0.onResult Callback called with result on each execution step
* @returns Returns last (already used) execution result
*/
async executeAll({ interval, onResult }: ExecuteAllArgs<RS>) {
while (true) {
this._result = this._engine.executeStep();
onResult && onResult(this._result);
if (!this._result.nextStepLocation) break;
await this.sleep(interval || 0);
console.log("Result: ", this._result);
if (!this._result.nextStepLocation) {
// End of program
onResult && onResult(this._result);
this._resolvePause && this._resolvePause(); // In case pause happens on same cycle
break;
} else if (this._resolvePause) {
// Execution has been paused/stopped
this._result.signal = "paused";
onResult && onResult(this._result);
this._resolvePause();
break;
} else {
onResult && onResult(this._result);
// Sleep for specified interval
await this.sleep(interval || 0);
}
}
return this._result;
}
/** Asynchronously sleep for a period of time */
private async sleep(millis: number) {
return new Promise<void>((resolve) => setTimeout(resolve, millis));
}