Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add detection for whether we are on an active Stopify stack #514

Merged
merged 2 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions stopify-continuations/src/runtime/abstractRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export abstract class RuntimeImpl implements Runtime {
// represents 'restore' mode.
mode: Mode;

// Represents whether the stack is currently active – that is, if you
// call a function if it can expect to do its capture/restore logic
// with the right things available on the stack.
stackActive: boolean;

/**
* A saved stack trace. This field is only used when a user-mode exception
* is thrown.
Expand All @@ -68,6 +73,7 @@ export abstract class RuntimeImpl implements Runtime {
this.stackSize = stackSize;
this.remainingStack = stackSize;
this.mode = true;
this.stackActive = false;
this.kind = undefined as any; // the worst
}

Expand All @@ -77,6 +83,7 @@ export abstract class RuntimeImpl implements Runtime {
f: () => {
this.stack = [];
this.mode = true;
this.stackActive = true;
return f();
},
this: this,
Expand All @@ -86,6 +93,7 @@ export abstract class RuntimeImpl implements Runtime {
runtime<T>(body: () => any, onDone: (x: Result) => T): T {

while(true) {
this.stackActive = true;
const result = this.abstractRun(body);

if (result.type === 'normal' || result.type === 'exception') {
Expand All @@ -110,19 +118,23 @@ export abstract class RuntimeImpl implements Runtime {
}
else if(result.type === 'normal') {
assert(this.mode, 'execution completed in restore mode');
this.stackActive = false;
return onDone(result);
}
else if(result.type === 'exception') {
assert(this.mode, `execution completed in restore mode, error was: ${result.value}`);
const stack = this.stackTrace;
this.stackTrace = [];
this.stackActive = false;
return onDone({ type: 'exception', value: result.value, stack });
}
}
else if (result.type === 'capture') {
this.stackActive = false;
body = () => result.f.call(global, this.makeCont(result.stack));
}
else if (result.type === 'restore') {
this.stackActive = false;
body = () => {
if (result.stack.length === 0) {
throw new Error(`Can't restore from empty stack`);
Expand All @@ -135,6 +147,7 @@ export abstract class RuntimeImpl implements Runtime {
};
}
else if (result.type === 'end-turn') {
this.stackActive = false;
return result.callback(onDone);
}
}
Expand Down
1 change: 1 addition & 0 deletions stopify-continuations/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface Runtime {

mode: Mode;
stack: Stack;
stackActive: boolean;

endTurn(callback: (onDone: (x: Result) => any) => any): never;

Expand Down
4 changes: 4 additions & 0 deletions stopify/src/runtime/abstractRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ export abstract class AbstractRunner implements AsyncRun {
this.continuationsRTS.runtime(body, callback);
}

isRunning(): boolean {
return this.continuationsRTS.stackActive;
}

processEvent(body: () => void, receiver: (x: Result) => void): void {
this.eventQueue.push({ body, receiver } );
this.processQueuedEvents();
Expand Down
1 change: 1 addition & 0 deletions stopify/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface AsyncRun {
onBreakpoint?: (line: number) => void): void;
pause(onPaused: (line?: number) => void): void;
resume(): void;
isRunning(): boolean;
setBreakpoints(line: number[]): void;
step(onStep: (line: number) => void): void;
pauseK(callback: (k: (r: Result) => void) => void): void;
Expand Down
32 changes: 32 additions & 0 deletions stopify/test/semantics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,38 @@ describe('integration tests', function () {
}
});

describe('Test cases that check running status',() => {
test('Running status should be paused (not running) in synchronous code after starting to run', onDone => {
const runner = harness(`
function sum(x) {
if (x % 20 === 0) { checkRunning(); }
if (x % 30 === 0) { pauseAndCheckRunning(); }
if (x <= 1) {
return 1;
} else {
return x + sum(x-1);
}
}
assert.equal(sum(100), 5050);
`, { captureMethod: 'lazy' });
runner.g.checkRunning = function() {
assert.equal(runner.isRunning(), true);
};
runner.g.pauseAndCheckRunning = function() {
runner.pauseK(k => {
assert.equal(runner.isRunning(), false);
k({ type: 'normal', value: 'restart' });
});
};
runner.run(result => {
expect(result).toEqual({ type: 'normal' });
onDone();
expect(runner.isRunning()).toBe(false);
});
}, 10000);

});

describe('Test cases that require deep stacks',() => {
const runtimeOpts: Partial<types.RuntimeOpts> = {
stackSize: 100,
Expand Down
Loading