sdk
sdk copied to clipboard
Native callables: Allow closing natively
I'm running into an issue with NativeCallable combined with NativeFinalizers. In some C libraries, one can provide a cleanup function when registering a callback. That cleanup function would get called when the native library determines it doesn't need the original callback anymore.
An example for this are user-defined functions in SQLite, where:
- I pass a
void (*xFunc)(sqlite3_context*,int,sqlite3_value**)for the actual function, and - a
void(*xDestroy)(void*)to get called when the SQLite wants to free the callback.
In Dart, I
- Create a
NativeCallable.isolateLocalto pass asxFunc, and - another
NativeCallable.isolateLocalto pass asxDestroy. The second native callable callsclose()on the first one and itself.
That worked fine for a long time, but I ran into crashes when adopting native finalizers. In particular, the order of events is:
- An isolate shuts down.
- A native finalizer for a SQLite isolate runs.
- SQLite calls
xDestroy, which now crashes the Dart VM withCannot invoke native callback while unwind error propagates.
This pattern of callback handling is not uncommon in C, but it seems like there is no easy way to safely dispose these callbacks in Dart. I have some ideas for convoluted workarounds, but I'm wondering if there could be away to simplify this. For instance, perhaps a native library could be allowed to close a NativeCallable:
- On
NativeCallable, there could be aPointer<Void> closeToken. - Additionally, there could be an
static Pointer<NativeFinalizerFunction> closeNativeentrypoint.
Then, I could pass the closeToken as a context to the native library and the closeNative function pointer as xDestroy, allowing SQLite to close the native callable.