4 ms·
You pretty much described how FFI works in .NET :) When you are passing a pointer across FFI which points to an object interior, you "pin" such object with `fi
by neonsunset 2y ago
You pretty much described how FFI works in .NET :)
When you are passing a pointer across FFI which points to an object interior, you "pin" such object with `fixed` statement which toggles a bit in the header of that object. Conveniently, it is also used by GC during the mark phase so your object is effectively pre-marked as live. Objects pinned in such way cannot be moved, ensuring the pointer to their interiors remains valid.
It's not as simple implementation-wise - .NET's GC has multiple strategies to minimize the impact of pinned objects on GC throughput and heap size. Additionally, for objects that are expected to be long-lived and pinned throughout their entire lifetime, there's a separate Pinned Object Heap to further improve the interoperability. In practical terms, this is not used that often because most of the time you pass a struct or a pointer to a struct on the stack that needs no marshalling or pinning. In the rare case the pointer needs to point to a long-lived pinned array, these are allocated with `GC.AllocateArray<T>(length, isPinned)`.
.NET has another interesting feature that is important to FFI: ByRefs which are special pointers that GC is aware of that can point to arbitrary memory. You can receive an int*, len from C and wrap it into a Span<int> (which would be ref int + length), and GC will be able to efficiently disambiguate that such pointer does not point to GC-owned heap and can be safely ignored while should it point to object memory, the memory range needs to be marked and byrefs pointing to it need to be updated if it is moved. That Span<int> can then be consumed by most standard library APIs that offer span overloads next to the array ones (there are more and more that accept just spans, since many types with contiguous memory can be treated as one).
This works the other way too and there is a separate efficient mechanism for pinning byrefs when passing them as plain pointers to C/C++/Rust/etc.