I suspect that the reason this function doesn't exist is that implementing it is complex and that few people need it. To be safe, you'd want pinning to work transitively, i.e., you'd want the entire graph of reachable objects to be pinned. But it doesn't seem like something that fundamentally can't be done.
E.g., suppose you have the following class:
[StructLayout(LayoutKind.Sequential)]
class SomeObject
{
public SomeObject r;
}
which you allocate like:
SomeObject o = new SomeObject();
and you try to pin it with:
GCHandle oh = GCHandle.Alloc(o, GCHandleType.Pinned);
you'll get the dreaded:
Object contains non-primitive or non-blittable data.
OK, fine, I can live with that. But suppose I had access to .NET's garbage collector implementation. What would be the obstacles? Here are the obstacles I see:
It seems to me that the GC already has to deal with some of these issues. So what am I forgetting?
NOTE: Before you ask "What are you trying to accomplish?", etc., the purpose of my asking is for research code, not necessarily limited to C#, and not necessarily limited to the CLR. I understand that fiddling with the runtime's own memory is not a typical scenario. In any case, this isn't a purely speculative question.
NOTE 2: Also, I don't care about marshaling. I'm just concerned about pinning.
The GC just knows that whatever you are going to next isn't going to work. You pin memory for a reason, surely it is to obtain a stable IntPtr to the object. Which you then next, say, pass to unmanaged code.
There is however a problem with the content of the pointed-to memory. It contains a pointer to a managed object. That pointer is going to randomly change whenever another thread allocates memory and triggers a collection. Which will play havoc on any code that uses the pinned memory content. There is no way to obtain a stable pointer, you can't "freeze" the collector. Pinning the pointer doesn't work either, it just passes the buck to the next pointed-to object. Hopefully it becomes null sooner or later, it would have to, but GC.Alloc doesn't travel the entire dependency graph to check that, there's no decent upper-bound on how long that can take. Getting an entire generation pinned is possible, that's a very hard deadlock.
Ugly problems, it was just much simpler to forbid it. Not much of a real problem, pinvoke happens everyday anyway.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With