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

runtime (scheduler/wasm): implement JS exit handling #3430

Draft
wants to merge 1 commit into
base: release
Choose a base branch
from
Draft
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: 1 addition & 1 deletion src/runtime/scheduler_any.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func run() {
go func() {
initAll()
callMain()
schedulerDone = true
postMain()
}()
scheduler()
}
Expand Down
9 changes: 9 additions & 0 deletions src/runtime/scheduler_end_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build wasi || !wasm

package runtime

func postMain() {
if hasScheduler {
schedulerDone = true
}
}
7 changes: 7 additions & 0 deletions src/runtime/scheduler_end_wasm_js.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build wasm && !wasi

package runtime

func postMain() {
proc_exit(0)
}
1 change: 1 addition & 0 deletions src/runtime/scheduler_none.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func run() {
initHeap()
initAll()
callMain()
postMain()
}

const hasScheduler = false
64 changes: 41 additions & 23 deletions targets/wasm_exec.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;

this._errExited = new Error("exited")
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
this.exited = true;
throw this._errExited;
};

const mem = () => {
// The buffer may change when requesting more memory.
return new DataView(this._inst.exports.memory.buffer);
Expand Down Expand Up @@ -281,13 +290,7 @@
fd_fdstat_get: () => 0, // dummy
fd_seek: () => 0, // dummy
"proc_exit": (code) => {
if (global.process) {
// Node.js
process.exit(code);
} else {
// Can't exit in a browser.
throw 'trying to exit with code ' + code;
}
this.exit(code);
},
random_get: (bufPtr, bufLen) => {
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
Expand All @@ -303,7 +306,17 @@
// func sleepTicks(timeout float64)
"runtime.sleepTicks": (timeout) => {
// Do not sleep, only reactivate scheduler after the given timeout.
setTimeout(this._inst.exports.go_scheduler, timeout);
setTimeout(() => {
try {
this._inst.exports.go_scheduler();
} catch (e) {
if (e == this._errExited) {
this._resolveExitPromise();
} else {
throw e;
}
}
}, timeout);
},

// func finalizeRef(v ref)
Expand Down Expand Up @@ -474,30 +487,34 @@

const mem = new DataView(this._inst.exports.memory.buffer)

while (true) {
const callbackPromise = new Promise((resolve) => {
this._resolveCallbackPromise = () => {
if (this.exited) {
throw new Error("bad callback: Go program has already exited");
}
setTimeout(resolve, 0); // make sure it is asynchronous
};
});
this._exit = new Promise((resolve) => {
this._resolveExitPromise = resolve;
});

try {
this._inst.exports._start();
if (this.exited) {
break;
} catch (e) {
if (e == this._errExited) {
this._resolveExitPromise();
} else {
throw e;
}
await callbackPromise;
}
await this._exit;
}

_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
this._resolveExitPromise();
try {
this._inst.exports.resume();
} catch (e) {
if (e == this._errExited) {
this._resolveExitPromise();
} else {
throw e;
}
}
}

Expand Down Expand Up @@ -525,6 +542,7 @@
}

const go = new Go();
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
return go.run(result.instance);
}).catch((err) => {
Expand Down