Skip to content
Open
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
2 changes: 2 additions & 0 deletions deps/v8/src/execution/isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3132,7 +3132,9 @@ void Isolate::Deinit() {
#endif // V8_ENABLE_WEBASSEMBLY

if (concurrent_recompilation_enabled()) {
printf("starting optimizing_compile_dispatcher_->Stop();\n");
optimizing_compile_dispatcher_->Stop();
printf("finished optimizing_compile_dispatcher_->Stop();\n");
delete optimizing_compile_dispatcher_;
optimizing_compile_dispatcher_ = nullptr;
}
Expand Down
3 changes: 3 additions & 0 deletions reproduction/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
/package-lock.json
/yarn.lock
26 changes: 26 additions & 0 deletions reproduction/from-gh-issue-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Issue described here:
// https://github.com/nodejs/node/issues/36616#issuecomment-757929741

// Any sufficiently large .wasm file
const wasmPath = require.resolve('@swc/wasm/wasm_bg.wasm');

const fs = require('fs');

// Workaround #36616
const timerId = setInterval(() => {}, 60000);

process.on('exit', () => {
console.log(new Date(), 'process exit');
});

process.on('beforeExit', () => {
console.log(new Date(), 'process beforeExit');
});

WebAssembly.compile(fs.readFileSync(wasmPath)).then(() => {
console.log(new Date(), 'compiled (liftoff)');
clearInterval(timerId);
if (process.env.EXPLICIT_EXIT) process.exit();
});

console.log(new Date(), 'WebAssembly.compile()');
9 changes: 9 additions & 0 deletions reproduction/from-gh-issue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue described here:
// https://github.com/nodejs/node/issues/36616#issue-774212139

// Any sufficiently large .wasm file
const wasmPath = require.resolve('@swc/wasm/wasm_bg.wasm');

const fs = require("fs")
const data = fs.readFileSync(wasmPath);
WebAssembly.compile(data).then(()=>console.log("module ready"))
34 changes: 34 additions & 0 deletions reproduction/messy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Messy script I have been using to demo the bug and verify the fix

const swcInvocations = +process.argv[2];
const setTimeoutMs = +process.argv[3];

process.on('beforeExit', () => {
console.log('beforeExit emitted');
});

process.on('exit', () => {
console.log('exit emitted');
console.log(JSON.stringify(performance.toJSON(), null, 2));
});

if(!Number.isNaN(setTimeoutMs)) {
console.log(`setting ${setTimeoutMs}ms timeout`);
setTimeout(() => {
console.log(`firing ${setTimeoutMs}ms timeout`);
}, setTimeoutMs);
}

console.time('require("@swc/wasm")');
const swc = require('@swc/wasm');
console.timeEnd('require("@swc/wasm")');

// any JS file; doesn't have to be this one. Just want to make SWC actually do some work in case this affects
// V8's optimization heuristics.
const src = require('fs').readFileSync(__filename, 'utf8');

console.time(`invoke swc ${ swcInvocations } times`);
for(let i = 0; i < swcInvocations; i++) {
swc.transformSync(src, {});
}
console.timeEnd(`invoke swc ${ swcInvocations } times`);
5 changes: 5 additions & 0 deletions reproduction/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"@swc/wasm": "^1.2.129"
}
}
17 changes: 17 additions & 0 deletions reproduction/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euxo pipefail

# This script runs 3x reproductions demonstrating a few flavors of this bug.
# I recommend using it as an example and instead running the following commands manually.

# Assuming you've built node from source and the binary resides here:
local_node=../out/Release/node

yarn
node ./from-gh-issue.js
node ./from-gh-issue-2.js
node ./messy.js 100

"$local_node" ./from-gh-issue.js
"$local_node" ./from-gh-issue-2.js
"$local_node" ./messy.js 100
36 changes: 32 additions & 4 deletions src/node_platform.cc
Original file line number Diff line number Diff line change
Expand Up @@ -441,10 +441,30 @@ void NodePlatform::DrainTasks(Isolate* isolate) {
std::shared_ptr<PerIsolatePlatformData> per_isolate = ForNodeIsolate(isolate);
if (!per_isolate) return;

do {
// Worker tasks aren't associated with an Isolate.
worker_thread_task_runner_->BlockingDrain();
} while (per_isolate->FlushForegroundTasksInternal());
// Note: does not execute delayed tasks
while(true) {
bool has_background_tasks = per_isolate->HasPendingBackgroundTasks();
bool did_foreground_work = per_isolate->FlushForegroundTasksInternal();

// Guaranteed no tasks remain, because background had nothing and foreground cannot possibly have posted to background
if(!has_background_tasks && !did_foreground_work) break;

// Background work is in-progress, and we're sure we did not execute the resulting tasks on the foreground.
// Wait for them to arrive
if(has_background_tasks && !did_foreground_work) {
per_isolate->WaitForNonDelayedForegroundTasks();
}

// Only unaccounted possibility is that we did_foreground_work; loop again
}
}

bool PerIsolatePlatformData::HasPendingBackgroundTasks() {
return isolate_->HasPendingBackgroundTasks();
}

void PerIsolatePlatformData::WaitForNonDelayedForegroundTasks() {
foreground_tasks_.Wait();
}

bool PerIsolatePlatformData::FlushForegroundTasksInternal() {
Expand Down Expand Up @@ -572,6 +592,14 @@ void TaskQueue<T>::Push(std::unique_ptr<T> task) {
tasks_available_.Signal(scoped_lock);
}

template <class T>
void TaskQueue<T>::Wait() {
Mutex::ScopedLock scoped_lock(lock_);
while (task_queue_.empty() && !stopped_) {
tasks_available_.Wait(scoped_lock);
}
}

template <class T>
std::unique_ptr<T> TaskQueue<T>::Pop() {
Mutex::ScopedLock scoped_lock(lock_);
Expand Down
5 changes: 5 additions & 0 deletions src/node_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class TaskQueue {
std::unique_ptr<T> Pop();
std::unique_ptr<T> BlockingPop();
std::queue<std::unique_ptr<T>> PopAll();
void Wait();
void NotifyOfCompletion();
void BlockingDrain();
void Stop();
Expand Down Expand Up @@ -80,6 +81,10 @@ class PerIsolatePlatformData :
// flushing.
bool FlushForegroundTasksInternal();

bool HasPendingBackgroundTasks();

void WaitForNonDelayedForegroundTasks();

const uv_loop_t* event_loop() const { return loop_; }

private:
Expand Down