web: reject concurrent worker runtime starts#55
Conversation
Name runtime_locked in the public error union and document its worker-start semantics for same-origin contention.
Guard worker startup with an origin-scoped Web Lock and serialize lifecycle RPCs in invocation order so ownership follows the daemon lifetime.
Exercise the same-origin two-tab failure, reject SQLite fallback markers, and prove Try again succeeds after the owning tab closes.
|
Thanks for digging into #48, @jgmcalpine. I'd been working on the same fix in parallel over in #56, and put a lot of time into weeding out the edge cases and race conditions around the lock lifecycle. Wanted to lay out where the two line up and where #56 does more. Where they overlap:
Where #56 goes further:
Given that extra coverage, we're going to go with #56. Really appreciate the effort here. |
Thanks for the detailed comparison, that makes sense. I appreciate you taking the time to explain the additional edge cases and coverage in #56. Glad the underlying issue is fixed, it has been really interesting digging into wavelength. |
Summary
runtime_lockederror classification for worker runtime contention.Technical Notes
The default worker transport now acquires a non-blocking, origin-scoped Web Lock immediately before dispatching the daemon
startrequest.The lock is held for the runtime's storage-owning lifetime and released after:
It remains held after a failed stop or when the daemon starts successfully but the subsequent
getInfo()request fails.Worker
startandstoprequests are serialized in invocation order so overlapping lifecycle operations cannot release the lock while a later runtime is starting.When another same-origin tab owns the lock, the second caller receives:
Browsers without
navigator.locksretain the existing behavior.This change does not modify the main-thread transport, SQLite runtime assets, lifecycle phases, wipe behavior, or the existing retry UI.
Steps to Test
Run the package checks:
Run the browser regression:
On a new Playwright installation, install Chromium first:
The two-page regression verifies that:
Manual QA
Build and serve the local demo:
pnpm --filter web-wallet-demo run wasm:fetch pnpm --filter web-wallet-demo run build cd apps/web-wallet-demo node smoke-server.jsThen open
http://127.0.0.1:8790in two tabs in the same browser profile:This wallet is already open in another tab. Close the other tab and try again.:memory:OPFS VFS unavailableSQLITE_CANTOPENResult
Tab B fails before SQLite startup and displays the actionable runtime contention message:
Related Issues & Pull Requests
Closes #48
Related to #49