Skip to content

dart:ffi GC finalizers #35770

@dcharkes

Description

@dcharkes
Contributor

Update 2022:

Available in https://api.dart.dev/stable/2.18.3/dart-ffi/NativeFinalizer-class.html.

Update 2020-06-26:
Finalizers are now available in native code (not yet exposed in Dart):

d.lookupFunction<Void Function(Handle), void Function(Object)>("PassObjectToC");

void attachFinalizer(Object o) => passObjectToC(o);
static void RunFinalizer(void* isolate_callback_data,
                         void* peer) {
  printf("Running finalizer.\n");
}

extern "c" void PassObjectToC(Dart_Handle h) {
  void* peer = 0x0;
  intptr_t size = 8;
  auto finalizable_handle = Dart_NewFinalizableHandle_DL(handle_2, peer, size, RunFinalizer);
}

Use dynamic linking (the _DL suffix) so that the symbols are available in Flutter. See documentation and samples on 7eac9f3 (C code, Dart code).

See more documentation in native_api.h and dart_api_dl.h.

Caveat:

  • The dart optimiser can inline method bodies and fields, which means objects can in some cases be GCed (and their finalizer run) before all methods are done executing. When we expose finalizers in Dart, we will have a solution for this. Workaround: do an ffi call with a Dart_Handle passing that object to native, that will keep it alive until that call (like a reachability fence). Or alternatively use the Dart calling convention to keep the object alive:
    @pragma('vm:never-inline')
    Object reachabilityFence(Object obj) {
      return obj;
    }

==========================================================================================

This issue was originally intended to track exposing finalizers in Dart. (The above code exposes finalizers in C.)

A potential API would be.

/// Return a pointer object that has a finalizer attached to it. When this
/// pointer object is collected by GC the given finalizer is invoked.
///
/// Note: the pointer object passed to the finalizer is not the same as 
/// the pointer object that is returned from [finalizable] - it points
/// to the same memory region but has different identity. 
Pointer<T> finalizable<T>(Pointer<T> p, void finalizer(Pointer<T> ptr)) {

}

However, exposing FFI-specific finalizers in Dart has been superseded by adding finalizers to Dart in general https://github.com/dart-lang/sdk/issues/45455.

Activity

added this to the Dart VM FFI 1.0 milestone on Jan 25, 2019
added
area-core-librarySDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.
on Jan 25, 2019
dcharkes

dcharkes commented on Feb 18, 2019

@dcharkes
ContributorAuthor

As discussed with @jonasfj and @mkustermann, maybe it would be better to provide a Pointer to a C function as finalizer, as the GC does currently not support running Dart code for finalization.

class Pointer {
  attachFinalizer(Pointer<NativeFunction<void Function(Pointer)>> finalizerPointer) {

  }
}
jonasfj

jonasfj commented on Feb 18, 2019

@jonasfj
Member

You might also want to include a sizeHint so we can signal an approximate size of the object to the GC.
Even if I don't know the exact size of the object I'm holding on to, I know if it's approximately 12 bytes, 12kb, or 12mb give or take a few orders of magnitude.

removed this from the Dart VM FFI 1.0 milestone on Mar 11, 2019
sjindel-google

sjindel-google commented on Aug 5, 2019

@sjindel-google
Contributor

We should implement this in the initial release since so many users seem to need it.

sjindel-google

sjindel-google commented on Aug 26, 2019

@sjindel-google
Contributor

I think we should estimate 3 weeks for this, given that we need:

  • Support for attaching finalizers to Pointer (based on existing finalizer support).
  • Maintaining a reference to the original from derived pointers.
  • Maintaining a reference to the original from external typed data.
yulingtianxia

yulingtianxia commented on Oct 14, 2019

@yulingtianxia

We should implement this in the initial release since so many users seem to need it.

Yeah, we really need it.

ds84182

ds84182 commented on Nov 5, 2019

@ds84182
Contributor

Looking at https://dart-review.googlesource.com/c/sdk/+/123662, I don't think this solves all issues, especially with long lived structures or strings in a static method.

For example, libpng's png_create_read_struct needs the version string of libpng from link-time to ensure that ABI-incompatible changes haven't occurred (https://github.com/glennrp/libpng/blob/libpng16/png.h#L922). Yes, you could also wrap the function with another C library to forward the version correctly, but then you have to make sure that Dart, the auxiliary native library, and libpng are all in sync and are ABI compatible.

I'm just hoping that that CL isn't the final design for pointer finalizers.

derolf

derolf commented on Dec 11, 2019

@derolf

When will this feature be available?

42 remaining items

mraleph

mraleph commented on May 17, 2021

@mraleph
Member

@vaind I think the best you can do right now is the following:

@pragma('vm:never-inline')
Object reachabilityFence(Object obj) {
  return obj;
}

and use this as a replacement for dart_keepalive. That should keep the obj alive (though it will break if we start removing parameters which are never used from functions in AOT compiler, but we don't have any immediate plans to work on something like that AFAIK).

vaind

vaind commented on May 17, 2021

@vaind

Many thanks @mraleph, you saved my day!

skquo

skquo commented on May 22, 2021

@skquo

Update 2020-06-26:
.....

Expose finalizers in Dart.

/// Return a pointer object that has a finalizer attached to it. When this
/// pointer object is collected by GC the given finalizer is invoked.
///
/// Note: the pointer object passed to the finalizer is not the same as 
/// the pointer object that is returned from [finalizable] - it points
/// to the same memory region but has different identity. 
Pointer<T> finalizable<T>(Pointer<T> p, void finalizer(Pointer<T> ptr)) {

}

What does it mean @dcharkes ? Where can we find finalizable?

bayo-code

bayo-code commented on May 23, 2021

@bayo-code

There's lots of things about this Finalizer API that I don't understand. If you could help me clarify some of these, I'll be grateful...

  1. According to my understanding, a Finalizer is a function we can attach to a Dart handle (in C) that will be called when the object is garbage collected. Is this right?
  2. To create a Finalizer, I have to pass the Dart object itself to native code as a Handle (which is Dart_Handle in C), which will then register a function that Dart can call to "finalize" whatever memory the native code is allocated. However, the Dart_NewPersistentHandle function returns a Dart_PersistentHandle. What am I supposed to do with it, pass it back to the Dart class that called the function registration routine? If that's the case, does that mean that I would not need the initial object (or pointer) that points to native memory and will instead use whatever is returned by the Dart_NewPersistentHandle in Dart?

I'm sorry if these questions sound dumb, it's just that I'm not familiar with the whole Finalizer thing, and I'm trying to make sure that I don't leak memory when I'm working with native code cause I'm creating lots of them

fei4xu

fei4xu commented on May 25, 2021

@fei4xu

I don't like this design and I'm confused: do I need to change the .c code in order to call it from dart? what if the .c lib is a 3rd party library which I don't have source code ?

dcharkes

dcharkes commented on May 26, 2021

@dcharkes
ContributorAuthor

Do I need to change the .c code in order to call it from dart? what if the .c lib is a 3rd party library which I don't have source code ?

You can write a C library that wraps the original library which attaches the finalizers. And yes, that is not ideal. Multiple packages are facing this issue.

That's why we're investigating adding finalizers in Dart-land. (This issue, #35770, is tracking adding FFI-specific Dart finalizers. However, https://github.com/dart-lang/sdk/issues/45455 proposes to add finalizers to Dart in general.)

Until one of those two issues has been resolved, your options are:

  • Finalizers in C.
  • Do memory management in another way. For example with (1) lexical scoping through arena from package:ffi, (2) manual memory management by adding free calls in your code, or (3) providing a release method in your API that users have to call when done. (Of course there are scenarios in which none of these three work, for example when your objects get passed to a framework that does not tell you when it's done using your object.)
dcharkes

dcharkes commented on May 26, 2021

@dcharkes
ContributorAuthor
  1. According to my understanding, a Finalizer is a function we can attach to a Dart handle (in C) that will be called when the object is garbage collected. Is this right?

Yes.

  1. To create a Finalizer, I have to pass the Dart object itself to native code as a Handle (which is Dart_Handle in C), which will then register a function that Dart can call to "finalize" whatever memory the native code is allocated. However, the Dart_NewPersistentHandle function returns a Dart_PersistentHandle.

I presume you meant Dart_NewWeakPersistentHandle function returns a Dart_WeakPersistentHandle.

Or Dart_NewFinalizableHandle which returns a Dart_FinalizableHandle.

What am I supposed to do with it, pass it back to the Dart class that called the function registration routine?

Dart_FinalizableHandle and Dart_PersistentHandle are used to remove the finalizer if you would want to remove it.

If you don't ever want to cancel it, you can ignore the return value of Dart_NewFinalizableHandle, because it gets automatically deleted when the object is garbage collected.

You should not disregard a Dart_WeakPersistentHandle that would leak memory. So I suggest you use Dart_FinalizableHandles instead.

If that's the case, does that mean that I would not need the initial object (or pointer) that points to native memory and will instead use whatever is returned by the Dart_NewPersistentHandle in Dart?

I presume you meant Dart_NewWeakPersistentHandle or Dart_NewFinalizablePersistentHandle here.

You can pass the Dart_NewWeakPersistentHandle or Dart_NewFinalizablePersistentHandle back to the Dart class and store it there as a Pointer. If you'd ever want to cancel the finalizer, you would use that Handle the cancel the finalizer.

I'm sorry if these questions sound dumb, it's just that I'm not familiar with the whole Finalizer thing, and I'm trying to make sure that I don't leak memory when I'm working with native code cause I'm creating lots of them

No problemo!

P.S. These kind of questions are not really issues with Dart. So StackOverflow might be a better place for them than making this GitHub thread very long!

dcharkes

dcharkes commented on May 26, 2021

@dcharkes
ContributorAuthor

Update 2020-06-26:
.....
Expose finalizers in Dart.

/// Return a pointer object that has a finalizer attached to it. When this
/// pointer object is collected by GC the given finalizer is invoked.
///
/// Note: the pointer object passed to the finalizer is not the same as 
/// the pointer object that is returned from [finalizable] - it points
/// to the same memory region but has different identity. 
Pointer<T> finalizable<T>(Pointer<T> p, void finalizer(Pointer<T> ptr)) {

}

What does it mean @dcharkes ? Where can we find finalizable?

This does not exist. This was a proposed API. That will likely never exist because of the pursuit of adding Dart non-FFI-specific finalizers: #45455.

dcharkes

dcharkes commented on May 26, 2021

@dcharkes
ContributorAuthor

Given that C finalizers are available, and Dart finalizers are tracked in #45455, I'm going to lock this issue for now.

If you have questions about how to use the finalizers in C, please post your questions on StackOverflow.

If you want to chime in on the Dart finalizer design discussion, please post in #45455.

If for whatever reason #45455 does not come to be, I'll unlock this discussion again.

locked and limited conversation to collaborators on May 26, 2021
vsmenon

vsmenon commented on Oct 31, 2022

@vsmenon
Member

@dcharkes - are we ready to close this issue now?

dcharkes

dcharkes commented on Oct 31, 2022

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-core-librarySDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.library-ffi

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @kevmoo@mraleph@jonasfj@yulingtianxia@derolf

        Issue actions

          dart:ffi GC finalizers · Issue #35770 · dart-lang/sdk