Skip to content
This repository has been archived by the owner on Jan 28, 2024. It is now read-only.

Listener/Async Blocks #601

Merged
merged 8 commits into from
Aug 30, 2023
Merged

Listener/Async Blocks #601

merged 8 commits into from
Aug 30, 2023

Conversation

liamappelbe
Copy link
Contributor

@liamappelbe liamappelbe commented Aug 29, 2023

Add support for listener blocks, built on NativeCallable.listener. Also, bump the SDK version to 3.2 so we get the keepIsolateAlive field (otherwise the internal async trampoline will prevent the isolate from closing).

All the substantive changes are in lib/src/code_generator/objc_block.dart. Block.listener uses all the same infrastructure as Block.fromFunction, even using the exact same registerClosure and closureTrampoline functions. The only difference is that listeners wrap the closureTrampoline in a NativeCallable.listener, whereas ordinary Blocks wrap that same function in a Pointer.fromFunction.

@liamappelbe liamappelbe changed the title WIP: Listener Blocks Listener/Async Blocks Aug 30, 2023
@liamappelbe liamappelbe marked this pull request as ready for review August 30, 2023 03:29
Copy link
Contributor

@dcharkes dcharkes left a comment

Choose a reason for hiding this comment

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

LGTM.

Should we maybe add some integration test with a real world API that needs this?

(Nit, I stand by #363 (comment), I now had to review the generator instead of the generated API. 👓 )

final thread = BlockTester.callOnNewThread_(lib, block);
thread.start();

await hasRun.future;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice! 🚀

@@ -591,7 +591,7 @@ class StringConfigSpec<RE extends Object?> extends ConfigSpec<String, RE> {
if (!o.checkType<String>(log: log)) {
return false;
}
if (_regexp != null && !_regexp!.hasMatch(o.value as String)) {
if (_regexp != null && !_regexp.hasMatch(o.value as String)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

field promotion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed this because when I bumped the SDK version the analyzer started complaining about it. My guess is that in the new SDK this field promotion is ok because it's a private field. The thing that makes field promotion impossible is that other libraries could override the getter in ways that would break the null safety inferences, but they can't do that if it's a private field.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Better fix: #605

/// This is based on FFI's NativeCallable.listener, and has the same
/// capabilities and limitations. This block can be invoked from any thread,
/// but only supports void functions, and is not run synchronously. See
/// NativeCallable.listener for more details.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably have some documentation about not keeping the isolate alive.

Also, are there use cases in which users would want to keep the isolate alive? Should we surface a bool on the API?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment updated.

We could do that, but note that ObjCBlock.listener doesn't have a 1:1 mapping with a NativeCallable.listener. All ObjCBlock.listener in an isolate use the same NativeCallable.listener trampoline. So it's not a simple matter of plumbing through that bool.

Instead we'd need to manually implement a way to keep the isolate alive (probably creating a RawReceivePort that we don't actually use). This is something that the user can already do if they want, but it's probably worth doing anyway, for ergonomics. Maybe we should do this for all blocks, not just listeners. But it's blocked on https://github.com/dart-lang/ffigen/issues/428 atm. I'll file a bug.

.github/workflows/test-package.yml Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
pubspec.yaml Outdated Show resolved Hide resolved
@@ -13,7 +15,7 @@ topics:
- codegen

environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.2.0-114.0.dev <4.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reason for the generator (ffigen) to require the new SDK version?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Listener blocks are built on NativeCallable.listener. While that feature did land in 3.1, there are some important updates to it in 3.2 that we need (specifically keepIsolateAlive = false).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants