Skip to content

Commit 0231755

Browse files
committed
wasm: reduce memory usage after crash
If a Process crashes, we continue to hold references to the WebAssembly heap. We will only reclaim the memory if all the DocumentLinter-s referring to that Process are modified by the user (triggering a re-lint, triggering crash recovery). Reduce memory usage by promptly reclaiming memory on crash.
1 parent 8433c75 commit 0231755

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

wasm/quick-lint-js.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -455,9 +455,6 @@ class Process {
455455
}
456456
let func = wasmInstance.exports[name];
457457
return (...args) => {
458-
if (process._crashedException !== null) {
459-
throw process._crashedException;
460-
}
461458
try {
462459
exports.maybeInjectFault(process, name);
463460
try {
@@ -467,7 +464,7 @@ class Process {
467464
}
468465
} catch (e) {
469466
if (e instanceof ProcessCrashed) {
470-
process._crashedException = e;
467+
process._taint(e);
471468
}
472469
throw e;
473470
}
@@ -492,6 +489,30 @@ class Process {
492489
return this._crashedException !== null;
493490
}
494491

492+
_taint(exception) {
493+
this._crashedException = exception;
494+
495+
function tainted() {
496+
throw this._crashedException;
497+
}
498+
499+
// Reduce memory usage.
500+
this._wasmInstance = null;
501+
this._heap = null;
502+
503+
// Make future calls crash and also reduce memory usage.
504+
this._malloc = tainted;
505+
this._free = tainted;
506+
this._vscodeCreateDocument = tainted;
507+
this._vscodeDestroyDocument = tainted;
508+
this._vscodeLint = tainted;
509+
this._vscodeReplaceText = tainted;
510+
this._webDemoCreateDocument = tainted;
511+
this._webDemoDestroyDocument = tainted;
512+
this._webDemoLint = tainted;
513+
this._webDemoSetText = tainted;
514+
}
515+
495516
toString() {
496517
return `Process(id=${this._idForDebugging})`;
497518
}

wasm/test-document.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,33 @@ describe("DocumentLinter", () => {
655655
"redeclaration of variable: y",
656656
]);
657657
});
658+
659+
it("untouched documents with crashed process do not leak memory", async () => {
660+
let numberOfDocuments = 200;
661+
let sourceLength = 100_000;
662+
let source = new Array(sourceLength + 1).join(" ");
663+
664+
let documentProcessManager = new DocumentProcessManager();
665+
let linters = [];
666+
for (let i = 0; i < numberOfDocuments; ++i) {
667+
let document = new MockDocument(source);
668+
crashProcessOnNextLint();
669+
let linter = disposeAfterTest(
670+
new DocumentLinter(document, documentProcessManager)
671+
);
672+
linters.push(linter);
673+
await linter.editorChangedVisibilityAsync();
674+
}
675+
676+
function crashProcessOnNextLint() {
677+
qljs.maybeInjectFault = (_process, functionName) => {
678+
if (functionName === "qljs_vscode_lint") {
679+
qljs.maybeInjectFault = originalMaybeInjectFault;
680+
throw new ProcessCrashed("(injected fault)");
681+
}
682+
};
683+
}
684+
}, /*timeout=*/ 30_000);
658685
});
659686

660687
describe("ExhaustiveRNG", () => {

0 commit comments

Comments
 (0)