From c98a03dfa57ee235372219ef295fe1f34ee4f58c Mon Sep 17 00:00:00 2001 From: Codinget Date: Sun, 30 Aug 2026 01:00:32 +0000 Subject: [PATCH] docs(tsconnect/wasm): record why the shutdown promise cannot be awaited Closing shutdownCh lets main return and the runtime exit, which races with makePromise invoking resolve, so the promise shutdown() hands back may never settle. The JS loader already ignores it and awaits the runtime's exit instead; say so here so the next reader does not take the unsettled promise for a bug and rewire the callers. Also note that the once and the channel now belong to the same instance. webnet/webnet#206 left the shared-channel race to this branch, and the one-IPN-per-runtime guard dissolves it. Co-Authored-By: claude-opus-5 --- cmd/tsconnect/wasm/wasm_js.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/tsconnect/wasm/wasm_js.go b/cmd/tsconnect/wasm/wasm_js.go index 67208be18..4d6a7c057 100644 --- a/cmd/tsconnect/wasm/wasm_js.go +++ b/cmd/tsconnect/wasm/wasm_js.go @@ -625,6 +625,11 @@ func (i *jsIPN) logout() { }() } +// shutdown tears down the backend and lets main return, which exits the whole +// Go runtime. Callers should await the runtime's exit rather than the promise +// returned here: closing shutdownCh races with makePromise resolving, so the +// promise may never settle. There is exactly one IPN per runtime, so the once +// and the channel belong to the same instance and a second call is a no-op. func (i *jsIPN) shutdown() js.Value { return makePromise(func() (any, error) { i.shutdownOnce.Do(func() {