The future: JSPI and unsafe access handles #115
Replies: 3 comments 3 replies
-
Incredible! |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
There's good news and bad news with JSPI. The good news is that the Chrome implementation doesn't crash so much on me, and Firefox has begun development. The bad news is now that it's stable enough to run benchmarks, Chrome seems to have a significant performance issue, at least on my platform (M2 Mac). Asynchronous calls from WebAssembly to JavaScript are faster with JSPI than Asyncify, but in the other direction, from JS to WASM, JSPI is horrendously slower. Hundreds of times slower. Here are comparison benchmarks with the same VFS:
The news on FileSystemSyncAccessHandle readwrite-unsafe locks is that they reach Chrome stable in a few weeks. |
Beta Was this translation helpful? Give feedback.
-
The teaser
How would you like a persistent browser database that...
The proposals
There are two web platform proposals of particular interest in the pipeline.
WebAssembly JavaScript Promise Integration (JSPI)
JSPI is basically a replacement for the original Asyncify that patched WASM to unwind and rewind the stack to accommodate asynchronous calls from WASM to JavaScript. The original Asyncify was clever and amazing, but had substantial size and performance penalties, especially with SQLite. JSPI uses stack switching to provide the same programming bridge, but without the penalties. This makes it a lot easier to connect SQLite with asynchronous JavaScript APIs (like OPFS, IndexedDB, Web Locks, etc.) without making painful tradeoffs.
JSPI is working now in Chrome 104+ behind a flag (#enable-experimental-webassembly-stack-switching).
Locking modes for file access handles
New locking modes for FileSystemSyncAccessHandle allow multiple access handles to be opened on the same file. Previously an OPFS file could have only one open access handle at a time. The key addition is new locking mode "readwrite-unsafe", which works a lot more like the POSIX file handles that C programs use. This makes it a lot easier and more performant to support multiple connections to a SQLite database on OPFS.
The locking modes are working now in Chrome 120+ (now in the Canary channel) behind a flag (#file-system-access-locking-scheme).
The prototype
Together, these proposals provide a straightforward path to a fully featured SQLite virtual file system on OPFS. I built SQLite replacing Asyncify with JSPI, and implemented an OPFS VFS in this wa-sqlite branch. For the purposes of this prototype, my goal was to get something working as quickly as possible so I hardwired the VFS rather than rewrite all the wa-sqlite glue that allows implementing VFS extensions in JavaScript. The code is in libopfs.c and libopfs.js, and here's the Makefile diff.
Currently the main demo (and probably nothing else) works on Chrome Canary with the required flags enabled. The hardest part was really just figuring out how to get the first JSPI call to work - everything after that was straightforward because the need for clever workarounds has magically vanished. I only implemented exclusive locking for now but there's no barrier to supporting the full locking model. I'm pretty sure that the shared memory methods needed for WAL can be added but I have not attempted it.
I'm using EMSDK 3.1.45 and Chrome 120.0.6053.0 (Official Build) canary (arm64). Note that because these proposals are not final, the combination of my code, Emscripten, and Chrome may be incompatible at different versions (e.g. I had to inject a small workaround because the latest EMSDK isn't pre-built for my platform).
The takeaway
Proposals are subject to change or abandonment, the approval process can be long, and universal browser support can take even longer. Nevertheless, there seems to be good momentum here. I feel hopeful that this means we'll finally get the WebSQL replacement everyone wanted.
One little detail: a key reason why building this prototype was so easy is that the wa-sqlite API was designed to accommodate Asyncify from the start. In particular, any API call that could have an asynchronous JavaScript call in its call graph is itself defined to be asynchronous. This fortuitously meant that swapping JSPI for Asyncify had almost no impact on both public and internal APIs. That might not be the case with other libraries - for example, the official SQLite WASM project treats all its WASM exports as synchronous - and that may cause async contagion headaches in this particular future for those libraries and applications that use them.
Beta Was this translation helpful? Give feedback.
All reactions