Description
Pointer
s can be converted into external TypedData
with the asTypedList
.
When sending messages to other isolates:
Pointer
s are simply an address sent to another isolate.- external
TypedData
s have their backing buffer copied and a free-finalizer attached.
This means that if a user calls asTypedData
first, and then sends to another isolate, all native resources are cleaned up correctly both in the case the message is delivered as well as when the message is not delivered. We're assuming Dart takes ownership of the native memory and attaches a finalizer to free the buffer once it's done using it by calling asTypedList
with a finalizer immediately on receiving the pointer from native code.
If the user sends the Pointer
to another isolate and converts it to a typed-data on the other end, attaching a finalizer there, the native resource does not get finalized if the message never arrives. But it is faster, because no copies of the backing buffer are made. (The user could start sending messages back and forth between both isolates to acknowledge ownership is transferred from one isolate to the other, but that leads to the byzantine generals problem.)
A third strategy is to use a reference counter in native code and let every isolate call the finalizer. However, this is racy, because the source isolate could GC it's reference to the native resource before a reference on the receiving isolate is created increasing the reference count. Then you'd want to increase the reference account before sending, but that would lead to leaked resources if the message never arrives.
I think we have multiple things we should address here.
- We should document the difference between sending
Pointer
s to other isolates and sendingTypedData
s created from pointers to other isolates. - We could consider adding
NativeFinalizer
s to sending messages that get run if the message is not delivered. That would enable users to implement the refcounter properly. - We could consider introducing something like a
IsolateGroupFinalizable
which allows one to attach aNativeFinalizer
which will run once every copy of theIsolateGroupFinalizable
object has been GCed. This would avoid the need for users to implement their own refcounter. (I feel like we discussed this before, but I can't find the relevant GitHub issue.)
I think this is a rather common question that has to be dealt with as soon as somewhat longer FFIcalls are involved that users would like to run on a helper isolate. @craiglabenz