-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggestion: Add Deno.UnsafeOwnerPointer for owned memory handling #13550
Comments
In a discussion related to (un)safety of passing buffers into FFI calls marked Currently, sending a buffer into a nonblocking FFI call is kind of UB. JavaScript retains the ability to read and write into the buffer while in Rust terms the buffer is sent exclusively to another thread (I think?). This is fertile ground for data races and UB of all sorts. This ties nicely into this The current
As it comes to passing |
I added a more wordy proposal with some code examples here: https://github.com/aapoalas/deno-stable-ffi |
Linking #12341 (comment) here. I think that FFI should be as "raw" and directly interoperable with JavaScript values as possible. |
EDIT: Somewhat better organized, though wordier proposal text available here.
At the very least with C++ FFI, it is common for some FFI functions to expect a pointer to a slice of memory that will be used as the "return value" of the function.
eg. Given the following C++ struct and class method:
The FFI calling convention (at least with System V ABI, ie. on Linux) is actually like this (using Rust to elaborate):
ie. The return value is void and the the C++ return value for
Address
has instead become a prepended mutable pointer. This pointer is expected to point to a memory large enough to writeAddress
into, in this case 40 bytes.In Deno, this can be done as follows:
but now one must be careful to never let
addressData
be garbage collected whileaddressPointer
still lives and is used.An obvious way to do this is this:
Now as long as the pointer lives, the data lives.
Memory allocation and deallocation
With an OwnedPointer, or the associated Uint8Array, it's clear that Deno has been the one to allocate the memory and should be the one to deallocate it. However, it is quite possible that whatever is saved behind the OwnedPointer has a destructor. Calling that destructor is obviously not automatically done by OwnedPointer, nor is that the point.
Calling a possible destructor needs to be done by the programmer. But at the very least with C++ this does not pose any special issue (beyond requiring manual steps), C++ does not try to free the memory of the
data
Uint8Array or any such nonsense. The destructor will (or should) only take any necessary steps to clean up memory that is not itself included in thedata
itself. So f.ex. pointers within thedata
pointing to internally allocated data, or calls to notify some library internal trackers about a removal of a class instance, these will be done by the destructor. Actual deallocation of the owned memory is left up to whoever called the destructor, as it should be.Internal vs user-land
It is easy to argue that
UnsafeOwnedPointer
can be implemented as a user-land extension and thus does not need to be included in Deno itself. I would argue that this is still a very useful thing to include in Deno core. For one, the unnecessary allocation ofUnsafePointer
can be avoided by directly using theof
op. Additionally, this may be a fairly regular use case, in which case first-class support is not at all a bad idea.The text was updated successfully, but these errors were encountered: