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

Why are there synchronous and asynchronous builds? #7

Closed
rhashimoto opened this issue May 8, 2021 · 2 comments
Closed

Why are there synchronous and asynchronous builds? #7

rhashimoto opened this issue May 8, 2021 · 2 comments
Labels
faq Frequently asked question

Comments

@rhashimoto
Copy link
Owner

rhashimoto commented May 8, 2021

WebAssembly currently doesn't have native support for coroutines or continuations. That makes it difficult to connect SQLite with browser resources that are inherently asynchronous, like IndexedDB.

Asyncify is a clever workaround for that restriction. It postprocesses the .wasm file and adds some library support to effectively suspend some WebAssembly calls, run some asynchronous Javascript, and resume executing the WebAssembly. That can be used to bridge C's customarily blocking i/o, which SQLite uses, and the browser's asynchronous APIs (though not the only way).

Asyncify is amazing and awesome, but it isn't free. It makes the WebAssembly substantially larger and slower, particularly with SQLite which is something of a worst case. Developers can decide whether the costs are acceptable for their application, but the asynchronous build should only be used if it is actually needed.

JavaScript Promise Integration (aka JSPI or stack switching) is a replacement for Asyncify approaching stable release in Chrome and Firefox. JSPI builds have been added to dist/, and are only slightly larger (~1%) than synchronous builds. Benchmarks on pre-release browsers show performance roughly equivalent to Asyncify.

@jmatsushita
Copy link

Sorry for posting this in a issue, but I suppose it makes sense to move this thread (and maybe other faq labelled issues) into the Q&A section of discussions?

I'm wondering if, there would be a case to have non-promise based APIs (for instance exec()) for the sync build? I'm imagining that they could be used in the main thread with an :memory: database, or maybe in the future from a worker thread together with the "completely synchronous VFS approach you outline in #81

One of the advantages is to prevent the footgun (my foot unfortunately can attest) where you use the sync build thinking that something like that won't be an issue:

const query1 = sqlite3.exec(db, "SELECT sqlite_version()", (row) =>
  console.log("Result 1:", row)
);
const query2 = sqlite3.exec(db, "SELECT name FROM my_table", (row) =>
  console.log("Result 2:", row)
);

Whereas it will in fact be a race condition with both sync and async builds.

Something else I'm not sure I understand correctly is that in theory it shouldn't be possible (or a least desirable) to use the IndexedDB VFS (or any other async VFS for that matter) with the sync build. But AFAICT nothing really prevents a library user to do this? Could it be another footgun? Is the behavior undefined in that case?

Hopefully I'm understanding this correctly, I'm not so confident about the topic, so I appreciate clarifications 🙏 I completely understand if the maintenance burden is one of the reason this doesn't exist!

@rhashimoto
Copy link
Owner Author

@jmatsushita You're correct that I would prefer this to be in a Discussion. If you want to follow up further, please start a thread there. You're also correct that it makes more sense to have the whole FAQ in Discussions. By the time I realized that there were so many entries I was too lazy to move everything over.

I wanted the library API to be the same no matter what build you were using. It seems more confusing to me to have a function that returns a Promise only under some non-local circumstances.

That said, you're not the first person who has connected an asynchronous VFS to a synchronous build and received puzzling results. On the current dev channel build, using IDBBatchAtomicVFS with the default (synchronous) build now throws an exception:

Error: Synchronous WebAssembly cannot call async function

TBH I didn't actually mean to solve this problem; it just came out that way because of some JSPI constraints I had to work within.

I have also created a table of VFS behavior that includes which VFS works with which build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
faq Frequently asked question
Projects
None yet
Development

No branches or pull requests

2 participants