From 355a50da4a841cb1f335d738999f719377bd6a36 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Mon, 29 Jul 2024 21:32:18 -0700 Subject: [PATCH] Propagate runtime config options about stack size Deep stacks, at least on the lazy impl, seem to be working as long as the runtime correctly gets the options about stack size. Eager is still failing with call stack size exceeded with these changes. The runtimeOpts given to stopifyLocally were not used to configure any starting stack sizes, this does that configuration on the init() method. I'm not sure that's the right place, but it's where the estimator and some other config happen based on the opts field, so it seems reasonable. Separately, this also adds (optional) params to newRTS to configure stack size (since those are the parameters the constructors of the various runtimes take). With just this change (e.g. without the new configuration in init() that sets fields on rts), you can get deep/lazy working by using e.g. stopify.newRTS('lazy', 1000, 10) before calling .run(). But since stopifyLocally takes the runtimeOpts, it seems right to use them somewhere. --- stopify-continuations/src/runtime/runtime.ts | 10 +++++----- stopify/src/runtime/abstractRunner.ts | 5 +++++ stopify/test/semantics.test.ts | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/stopify-continuations/src/runtime/runtime.ts b/stopify-continuations/src/runtime/runtime.ts index fe02ecd3..0181ba50 100644 --- a/stopify-continuations/src/runtime/runtime.ts +++ b/stopify-continuations/src/runtime/runtime.ts @@ -11,17 +11,17 @@ import { FudgeRuntime } from './fudgeRuntime'; export * from './abstractRuntime'; let savedRTS: RuntimeImpl | undefined; -export function newRTS(transform: string): RuntimeImpl { +export function newRTS(transform: string, stackSize: number = Infinity, restoreFrames: number = Infinity): RuntimeImpl { if (!savedRTS || savedRTS.kind !== transform) { switch (transform) { // Default to shallow runtime. case 'catch': - savedRTS = new LazyRuntime(Infinity, Infinity); + savedRTS = new LazyRuntime(stackSize, restoreFrames); savedRTS.kind = 'catch'; // TODO(arjun): Sloppy break; - case 'lazy': savedRTS = new LazyRuntime(Infinity, Infinity); break; - case 'eager': savedRTS = new EagerRuntime(Infinity, Infinity); break; - case 'retval': savedRTS = new RetvalRuntime(Infinity, Infinity); break; + case 'lazy': savedRTS = new LazyRuntime(stackSize, restoreFrames); break; + case 'eager': savedRTS = new EagerRuntime(stackSize, restoreFrames); break; + case 'retval': savedRTS = new RetvalRuntime(stackSize, restoreFrames); break; case 'fudge': savedRTS = new FudgeRuntime(); break; default: throw new Error(`bad runtime: ${transform}`); } diff --git a/stopify/src/runtime/abstractRunner.ts b/stopify/src/runtime/abstractRunner.ts index 8c7a681e..0b78d7c1 100644 --- a/stopify/src/runtime/abstractRunner.ts +++ b/stopify/src/runtime/abstractRunner.ts @@ -59,6 +59,11 @@ export abstract class AbstractRunner implements AsyncRun { */ init(rts: Runtime) { this.continuationsRTS = rts; + + this.continuationsRTS.stackSize = this.opts.stackSize; + this.continuationsRTS.restoreFrames = this.opts.restoreFrames; + this.continuationsRTS.remainingStack = this.opts.stackSize; + const estimator = makeEstimator(this.opts); this.suspendRTS = new RuntimeWithSuspend(this.continuationsRTS, this.opts.yieldInterval, estimator, () => { switch (this.mayYieldFlag.kind) { diff --git a/stopify/test/semantics.test.ts b/stopify/test/semantics.test.ts index 8c5f0aee..cb63b8d7 100644 --- a/stopify/test/semantics.test.ts +++ b/stopify/test/semantics.test.ts @@ -290,7 +290,7 @@ describe('Test cases that require deep stacks',() => { yieldInterval: 25 }; - test.skip('non-tail recursive function (deep, lazy)', onDone => { + test('non-tail recursive function (deep, lazy)', onDone => { const runner = harness(` function sum(x) { if (x <= 1) { @@ -299,7 +299,7 @@ describe('Test cases that require deep stacks',() => { return x + sum(x-1); } } - assert.equal(sum(200), 1250025000); + assert.equal(sum(100000), 5000050000); `, { captureMethod: 'lazy' }, runtimeOpts); runner.run(result => { expect(result).toEqual({ type: 'normal' }); @@ -316,7 +316,7 @@ describe('Test cases that require deep stacks',() => { return x + sum(x-1); } } - assert.equal(sum(200), 1250025000); + assert.equal(sum(100000), 5000050000); `, { captureMethod: 'eager' }, runtimeOpts); runner.run(result => { expect(result).toEqual({ type: 'normal' });