Skip to content

docs should clearly detail that objective-c callbacks are scoped to an isolate #3264

Closed

Description

@brianquinlan

The documentation should explain that the VM will crash if an Objective-C callback occurs after the exit of the isolate that registered the callback.

Maybe this would the appropriate place to add the documentation:
https://dart.dev/interop/objective-c-interop#callbacks-and-multithreading-limitations

Activity

  1. removed
    needs-triageNeeds triage from the team. Remove this label once triaged.
    on Mar 26, 2026
  2. dcharkes commented on Mar 26, 2026

    @dcharkes
    Collaborator

    Yes, and the documentation should state that you should unregister your native callbacks on isolate exit. This can be done via NativeFinalizer.

    E.g. if you do an HTTP request, and the native code is going to stream packets to Dart. In your NativeFinalizer you need to call your native cancel_http_request so that it stops streaming packets to Dart and stops doing callbacks.

    Side note: The native finalizer approach will not work properly yet on Flutter with hot restart on helper isolates if you have singletons. E.g. if you have a native_register and native_unregister, and you call unregister on a helper isolate when it is shut down, then a new helper isolate might be started before the old helper isolate has shut down. Calling you to call native_register first, which is probably undefined behavior for your native code. More details: flutter/flutter#75528 (comment)

  3. brianquinlan commented on Mar 27, 2026

    @brianquinlan
    ContributorAuthor

    Yes, and the documentation should state that you should unregister your native callbacks on isolate exit. This can be done via NativeFinalizer.

    E.g. if you do an HTTP request, and the native code is going to stream packets to Dart. In your NativeFinalizer you need to call your native cancel_http_request so that it stops streaming packets to Dart and stops doing callbacks.

    1. I think that the premise of our objective-c interop is that the user does not have to write native code
    2. Wouldn't cancel_http_request (or whatever) be racy, i.e., if the callback is dispatched after the isolate becomes unuseable before the finalizer completes?

    Side note: The native finalizer approach will not work properly yet on Flutter with hot restart on helper isolates if you have singletons. E.g. if you have a native_register and native_unregister, and you call unregister on a helper isolate when it is shut down, then a new helper isolate might be started before the old helper isolate has shut down. Calling you to call native_register first, which is probably undefined behavior for your native code. More details: flutter/flutter#75528 (comment)

  4. liamappelbe commented on Mar 30, 2026

    @liamappelbe
    Contributor
    1. I think that the premise of our objective-c interop is that the user does not have to write native code

    That's more of an aspiration than a premise. If this is a case where native code is necessary, so be it.

    2. Wouldn't cancel_http_request (or whatever) be racy, i.e., if the callback is dispatched after the isolate becomes unuseable before the finalizer completes?

    You'd need to make the finalizer blocking. The metadata entries aren't cleaned up until after the finalizers run, so blocking the final isolate cleanup until your HTTP client is confirmed closed should at least prevent crashes 1.

    You could still have a memory leak if the callback is sending an object, and it never arrives (because the caller increments the ref count and the receiver decrements it). Hopefully this would be a rare edge case, because I'm struggling to think of a solution. Everything I think of just makes the edge case a bit more edgy.

    Footnotes

    1. The crash happens during callback dispatch, on the caller thread, where the callback's target port etc is looked up. If its metadata entry still exists then this will work fine even if the target isolate is mid-shutdown. The message will just silently never be handled. ↩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions