From bdb8a7de8deebdc78e0b86f2eb156d3b7da2502a Mon Sep 17 00:00:00 2001 From: Hossein Yousefi Date: Thu, 28 Nov 2024 10:38:12 +0100 Subject: [PATCH] [jnigen] Add doc about threading (#1751) * Close #611 --- pkgs/jnigen/doc/threading.md | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 pkgs/jnigen/doc/threading.md diff --git a/pkgs/jnigen/doc/threading.md b/pkgs/jnigen/doc/threading.md new file mode 100644 index 000000000..01fbb8a63 --- /dev/null +++ b/pkgs/jnigen/doc/threading.md @@ -0,0 +1,67 @@ +## Threading considerations + +Unlike method channels, JNIgen uses FFI calls. This means that the calls happen +on the calling thread. + +Dart callbacks are executed on the isolate where they were created. This means +the Dart isolate remains active until the associated Java objects are garbage +collected by the JVM. + +Java calls Dart callbacks directly when both are on the same thread. If they are +on different threads, Java sends a message to the Dart isolate and waits for a +response. However, you can +[configure void-returning callbacks to avoid waiting](<(../interface_implementation.md#implement-as-a-listener)>), +as they don't produce a return value. + +### Deadlocks + +When implementing Java/Kotlin interfaces in Dart, it is possible to create +deadlocks. Suppose we have created an object that implements `Runnable` in Dart +in the main isolate. This means that the code will always be run on the platform +thread. + +```dart +// Dart +final runnableFromDart = Runnable.implement( + $Runnable(run: () => print('hello')) +); +``` + +If Java creates a second thread from the platform thread and calls +`runnableFromDart.run()` from that thread and then attempts to join or +synchronize with the main thread, it will cause a deadlock. + +This is because the body of the `runnableFromDart` needs to be run on the +platform thread, so JNIgen waits until platform thread is available. However in +this setting the platform thread will not be available until +`runnableFromDart.run` is executed. + +If the callback does not need to be blocking, +[making it a listener](../interface_implementation.md#implement-as-a-listener) +solves this issue: + +```dart +// Dart +final runnableFromDart = Runnable.implement($Runnable( + run: () => print('hello'), + run$async: true, +)); +``` + +Of course, only void-returning methods can be non-blocking. + +### Calling thread restricted APIs + +When developing for Flutter Android, certain APIs can be thread-restricted to +the platform thread which is currently different than the Flutter's UI thread. + +These two threads will be merged in +[the near future](https://github.com/flutter/flutter/issues/150525). + +Until then you can wrap your calls with `runOnPlatformThread` which is available +in `dart:ui`. + +### Dart-standalone + +On Dart-standalone, call `Jni.setDylibsDir` in each new isolate, since each +isolate loads the dynamic libararies separately.