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

[jnigen] Add doc about threading #1751

Merged
merged 2 commits into from
Nov 28, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions pkgs/jnigen/doc/threading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## Threading considerations

Unlike method channels, JNIgen uses FFI calls. This means that the calls happen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you expand on this explanation, I think it'll make the section below more clear. My understanding is that jnigen calls the Dart callback directly if they're already on the correct thread, and dispatches a message to the target isolate if not. And in the dispatching case the callback can be configured to wait for a response or not. So your documentation should mention each of those cases.

Also mention that, in any case, the Dart callback is always run in the isolate it was created in (this is something users have been confused about). Maybe also add a note that in upcoming versions of flutter, the main isolate will always run on the platform thread (I couldn't find any docs about that, but you could link to this bug).

on the calling thread.

### 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.

### Dart-standalone

On Dart-standalone, call `Jni.setDylibsDir` in each new isolate, since each
isolate loads the dynamic libararies separately.