feat(tsconnect/wasm): add shutdown() to jsIPN #12
Reference in New Issue
Block a user
Delete Branch "shutdown-ipn-wasm"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Adds a
shutdown()method tojsIPNin the Go WASM bridge (cmd/tsconnect/wasm/wasm_js.go).Why
Without this, the Go WASM runtime is kept alive indefinitely after an IPN instance is no longer needed. The
main()goroutine blocks on a channel that is never closed, which:How
main()now blocks on ashutdownCh chan struct{}instead of<-make(chan bool). Closing the channel lets the Go runtime's main goroutine return, which terminates the WASM module.jsIPNgains three new fields:ln net.Listener(the safesocket listener),shutdownCh chan struct{}, andshutdownOnce sync.Once.run()(before spawning the goroutine) and stored asi.ln, soshutdown()can close it without a data race.jsIPN.shutdown()(exposed to JS viamakePromise):lb.Shutdown()— stops theLocalBackend, WireGuard engine, netstack, etc.i.ln— unblockssrv.Run's accept loop so the goroutine exits.i.shutdownCh— unblocksmain(), letting the Go runtime exit.All three are protected by
sync.Onceso the method is idempotent.log.Fatalfonipnserver.Runexit is demoted:net.ErrClosed(the expected error when the listener is closed during a clean shutdown) is silently ignored instead of crashing.AI disclosure
This commit was authored by Claude Code (Claude Sonnet 4.6).