Skip to content

feat: Add native bindings for sqlite3_interrupt and sqlite3_is_interrupted - #404

Open
dariyooo wants to merge 2 commits into
simolus3:mainfrom
dariyooo:feat-interrupt
Open

feat: Add native bindings for sqlite3_interrupt and sqlite3_is_interrupted#404
dariyooo wants to merge 2 commits into
simolus3:mainfrom
dariyooo:feat-interrupt

Conversation

@dariyooo

Copy link
Copy Markdown

Add bindings for sqlite3_interrupt and sqlite3_is_interrupted

Two declarations in assets/sqlite3.h, the rest is tool/generate_bindings.dart output. No public API, they only show up in unstable/ffi_bindings.dart, plus the used_symbols entries so the symbols survive a source: build. sqlite3_is_interrupted needs SQLite 3.41+, same situation as sqlite3_error_offset which is already bound.

I need it to cancel superseded queries in an interactive search. Nothing in Dart can stop a statement that already sits in sqlite3_step, since the isolate running it never gets to read a cancellation message and Isolate.kill has no safepoint to land on.

I left wasm out because that build is single threaded and every worker has its own memory, so nothing could call interrupt while a statement runs. Easy to add if you disagree.

What I'm actually after is drift interrupting the connection when a request is cancelled, so it can stop a statement that is already running instead of only skipping ones that have not started yet. The isolate doing that never owns the Database, it only has the handle address, so drift would have to go through unstable/ffi_bindings.dart. Is that fine to depend on, or would you rather expose something stable taking a Pointer<void>? I can move this to a drift issue if you'd rather keep it out of here.

@simolus3

Copy link
Copy Markdown
Owner

I left wasm out because that build is single threaded and every worker has its own memory, so nothing could call interrupt while a statement runs

I agree this doesn't make sense for WASM.

Is that fine to depend on, or would you rather expose something stable

I think this should be a stable API. We expose a separate interface for native-only methods that inherits from CommonDatabase, I think exposing interrupt() and bool get isInterrupted there might work?

What I'm actually after is drift interrupting the connection when a request is cancelled

This requires pretty significant changes to the isolate code in drift. Currently, isolates essentially act as servers running SQL statements, so the server isolate would be blocked for the entire duration of the connection.

We don't currently share actual SQLite connections across threads, which would be necessary if we want a client isolate to be able to interrupt a connection it doesn't own. Maybe we could add this API to the sqlite3_connection_pool package in this repository. I have plans to eventually make drift switch to be based on that package instead of manually setting up isolates.

Before landing this, it would be good to have a more complete plan for the approach here (especially how this would be wired up in drift). That can either be a WIP PR on drift depending on these unreleased changes, or at least some rough outline of an approach.

@dariyooo

dariyooo commented Sep 1, 2026

Copy link
Copy Markdown
Author

That works, I'll add interrupt() and isInterrupted to the native Database interface.

Claude and I had a look at sqlite3_connection_pool and it does look like the better home. PoolConnection.unsafePointer is already documented as safe to send across isolates, and there is already an abort concept with abortSignal and PoolAbortException, it only covers waiting for a lease today. Making an abort on a lease that is already running interrupt that connection looks like a continuation of that rather than a new concept.

The one thing that needs a decision is the rule that a pool connection must never be used concurrently. sqlite3_interrupt is documented as safe to call from another thread while a statement runs, so it would have to be an explicit exception to that rule.

Which of the two should I plan for? I would like to use this in an app in about 4-5 months. If drift moving onto the pool lands within that (or kinda close), I would rather wait for it and put the work into the pool and leaving drift's isolate code alone. If it is significantly further out, I would need the drift side changes and would look more closely at it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants