aapoalas / denonomicon

The Dark Arts of Deno Foreign Function Interface Programming
https://denonomicon.deno.dev/
MIT License
27 stars 0 forks source link

Expand examples to show FinalizationRegistry usage for binding GC and deallocation #12

Open audetto opened 1 month ago

audetto commented 1 month ago

I am trying to port a c++ library to deno and I am not clear on how gc with ffi buffers works.

This is a great explanation of how ffi works, but I cannot understand the connection with gc.

I saw this example

https://github.com/aapoalas/denonomicon/blob/5da9957a350e146aec203b93bb8621805569f8c0/examples/cpp/inheritedClass.ts#L225C7-L225C26

but I do not understand how the gc is informed that it needs to call a destructor when the buffer/pointer goes out of scope in deno.

I have done the same in node and a function like this

https://nodejs.org/api/n-api.html#napi_create_external

makes it super easy.

I could not understand what is the equivalent in deno.

aapoalas commented 1 month ago

Ah, you're absolutely correct that the GC isn't being informed here. I seem to have forgotten to do that binding here; this example was ... I don't quite remember but probably done as a quick aside.

Here's a more "complete" example (though with a C API, not C++, if that makes a difference to you): https://github.com/aapoalas/libclang_deno/blob/main/lib/mod.ts#L286-L308

So basically, I use a FinalizationRegistry that is given the pointer to hold, while the class instance "owning" the pointer is the weak value that controls the finalization lifetime. I also support explicit destruction via https://github.com/aapoalas/libclang_deno/blob/main/lib/mod.ts#L486-L497 which is just a manual call to the destructor and an unregister from the FinalizationRegistry to avoid double-free. This, IMO, the preferred way that FFI binding classes should be dropped but naturally in the JS world this is an odd API and fraught with danger, so the FinalizationRegistry is definitely a necessary "evil" backup.

aapoalas commented 1 month ago

Also just FYI, Deno does support N-API so you may want to consider that route if you're more familiar with it.

Though, of course, I love FFI to death.

audetto commented 1 month ago

I did not know. That would be the easiest since I have already written it.

Do you know which version? And in particular if it supports the new synchronous finalisers? Which are super good!

aapoalas commented 1 month ago

It's been declared stable for a good while so basically any version you might've downloaded recently.

I do not know about sync finalizers.

audetto commented 4 weeks ago

It does not look like they support it.

It is a fantastic new addition to node-api to call finalisers immediately when they guarantee not to interfere with the js environment.

Often, the only thing a ffi finaliser needs to do is to call delete ptr (or drop the ref count in some equivalent way), with no need to touch the js environment.

These cases now have a priority lane and get executed immediately.