From 41a8c2fb61730837c2e7c715cfd1a883f0d92b62 Mon Sep 17 00:00:00 2001 From: Daniel Dinu Date: Mon, 16 Sep 2019 00:03:11 -0700 Subject: [PATCH] fix(kernel): stack overflow in KernelHost.run() (#780) Fixes #778. This PR eliminates the mutual recursion between `KernelHost.run()` and `KernelHost.processRequest()` by scheduling the recursive `KernelHost.run()` call to run on the next iteration of the event loop. The code change has been tested as follows: - by running the stress-test script with and without the change to confirm that the crash occurs without the change and does not occur with the change; - by running the unit tests to confirm that behavior is consistent; - by synthesizing our internal CDK stack and confirming that the crash does not occur and that resources are generated correctly. --- packages/jsii-runtime/lib/host.ts | 6 +++++- packages/jsii-runtime/package.json | 2 +- packages/jsii-runtime/test/stress-test.ts | 26 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100755 packages/jsii-runtime/test/stress-test.ts diff --git a/packages/jsii-runtime/lib/host.ts b/packages/jsii-runtime/lib/host.ts index 0f64ec06ae..3caebb5246 100644 --- a/packages/jsii-runtime/lib/host.ts +++ b/packages/jsii-runtime/lib/host.ts @@ -14,7 +14,11 @@ export class KernelHost { return; // done } - this.processRequest(req, () => this.run()); + this.processRequest(req, () => { + // Schedule the call to run on the next event loop iteration to + // avoid recursion. + setImmediate(() => this.run()) + }); } private callbackHandler(callback: api.Callback) { diff --git a/packages/jsii-runtime/package.json b/packages/jsii-runtime/package.json index 0fcfb4df55..69d37d6740 100644 --- a/packages/jsii-runtime/package.json +++ b/packages/jsii-runtime/package.json @@ -10,7 +10,7 @@ "scripts": { "build": "tsc --build && chmod +x bin/jsii-runtime && /bin/bash ./bundle.sh", "watch": "tsc --build -w", - "test": "/bin/bash test/playback-test.sh", + "test": "/bin/bash test/playback-test.sh && node test/stress-test.js", "package": "package-js" }, "devDependencies": { diff --git a/packages/jsii-runtime/test/stress-test.ts b/packages/jsii-runtime/test/stress-test.ts new file mode 100755 index 0000000000..3af65107ec --- /dev/null +++ b/packages/jsii-runtime/test/stress-test.ts @@ -0,0 +1,26 @@ +import { InputOutput, Input, Output } from "../lib/in-out"; +import { KernelHost } from "../lib/host"; + +const requestCount = 250000; + +class FakeInputOutput extends InputOutput { + debug = false; + private count: number = 0; + + write(_: Output) { } + + read(): Input | undefined { + if(this.count == requestCount) { + return undefined; + } + + ++this.count; + return { api: 'stats' }; + } +} + +const inout = new FakeInputOutput(); +const host = new KernelHost(inout, { debug: false, noStack: false }); +host.run(); + +console.info("jsii-runtime stress test succeeded");