Reputation: 2270
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.
Upvotes: 6
Views: 4324
Reputation: 941970
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. GCHandle.Alloc doesn't travel the entire dependency graph to check that, there's no decent upper-bound on how long that can take. Circular references, perfectly legal in managed code, are very expensive to detect. Getting an entire generation pinned is technically 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.
Upvotes: 10