-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[vm/ffi] Introduce a NativeCallable.blocking
#54554
Comments
cc @mkustermann |
Been thinking a bit more about it. Maybe it would be better to be more explicit about the synchronization. The listener method could take an optional
It would be okay for me, if this machinery was actually visible on the interface, as it make it easy to re-use the synchronization primitives across calls, and also make it very apparent to users that they are dabbling in dead-lock land. |
We had discussions about this before. For example #52689 (comment): class NativeCallable {
/// Allocates a native callback resource to receive calls to the returned native function.
///
/// The native caller is blocked until the function result is available, at which point it's
/// passed back to the caller, which is then unblocked.
/// If the native call happens during a Dart call-out to the native code from the same
/// isolate which created the native callback, the execution may continue on the same
/// thread. Otherwise, that isolate will also be blocked until the code has run in
/// the isolate which did create the native callback.
///
/// Use [NativeCallback.close] to free the resource when no further calls are expected.
external static (NativeCallback, Pointer<NativeFunction<T>>) createBlockingCallback<T extends Function>
(@CorrespondingDartType("T") Function function);
} There's some questions: Define behavior when the isolate doesn't exist anymore (crash, block forever, simply return dummy C value). We kind of avoided this problem by saying: The isolate has to ensure it stays alive as long as C code may invoke the callback. There were also some concerns around deadlock possibilities, though it could be put into API docs that users have to use it with care. /cc @liamappelbe |
I guess the isolate will still be kept alive until the explicit close call. I just want to ensure the native-side call don't complete until the callback has run on the isolate event loop. I find it is often the case that native code expects the callback they invoke to run synchronously, but from an arbitrary thread. This poses a challenge currently.
So I end up having to wrap the native code in yet another layer of native code, to ensure the parameters passed to the callback survives until it actually runs, which is just a bit sad. Personally I'm fine with undefined behaviour, so crash, block forever, etc. For my use-case it is enough to support void functions, so I guess there is no possible dummy C value to return. |
Reading through #52689 (comment) I guess :
would serve my purpose, but actually I don't need the return value (so more like fire-and-forget), but I do need the native side to block, so that any |
Your described problem requires synchronous execution of a Dart callback on another thread. If we implement such a |
Yes indeed. I would be very happy if it supports return types as well. |
We are also in need of a 'NativeCallable.createBlockingCallback' function, so that our native code that runs the native callback inside a thread, waits for the dart callback to finish. |
NativeCallable.blocking
From a discussion with @HosseinYousefi. There could be usages of |
We can only solve this if we do thread pinning: (In practice I don't believe the thread changes. Otherwise |
That's orthogonal to this issue. Thread pinning means that an isolate is always going to run on one particular OS thread - even across event loop boundaries. What's described here is that if a 3rd party thread calls into Dart (via (Side note: This is already possible today in a limited way: If the isolate voluntarily stops running on one thread (possibly with active stack) and continues running the isolate on another thread. What we don't have is a) explicitly stopping an isolate from the side (without cooperation / action by the isolate) b) expose a Dart API to make use of this for callbacks.) Though taking a step back, I think we should align with the work on shared memory multithreading (/cc @mraleph @aam ) and see if we really want to expose a |
Ah,
I also believe it would be the right way. The reason I remarked on this is because dart-lang/native#1796 uses the other semantics and I'd like to have the option to replace that implementation with this if possible in the future.
👍 (That brings up an interesting question: If we have needs from JNIgen/FFIgen to do these kind of callbacks, would it solve those use cases to force those callbacks to be to shared isolates. We'd probably need go through an actual use case there to know the answer. E.g. What if some data needs to be main isolate / platform thread, which is not going to be shared.) |
Currently we have
However, it is often the case that a native callback expects to be synchronous in that the parameters parsed are really stack allocated values, yet we still need to ensure the callback is ferried to the correct isolate event loop on the dart side.
So I suggest to add the option to block the callback on the native side until completed in the correct isolate on the dart side. It is okay for my use-case to still restrict to functions returning Void.
Would it be possible to implement such behaviour? I envision something like:
Currently, if
native_error
is really an address of a stack allocated native object, this would fail.The text was updated successfully, but these errors were encountered: