Search code examples
.netgarbage-collectionmanagedref

Recover containing GC object from managed 'ref' interior pointer


This question is newly relevant in light of the new ref locals and ref return features in the latest versions of C# 7:

With the increased prominence and wider use of managed--or "interior"--pointer variables in C#, occasionally you may need to recover the respective containing Pinnable GC object for such a pointer. For example, if you are passing around a managed pointer to an array element of type T, you might need the array reference T[] itself in order to call (e.g.) Array.Copy(...).

So, from managed code, is there any reasonably legitimate way to recover the containing GC object handle, given either of the following prevalent interior/managed pointer (ref, out, in) uses:

  1. Interior pointer to a (struct or class) field within a GC object instance;
  2. Managed pointer to a (struct or class) element T of Array T[].

Internally in .NET, it appears that the GC uses the following function: /coreclr/master/src/gc/gc.cpp

#ifdef INTERIOR_POINTERS
// will find all heap objects (large and small)
uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
{
    ....

This code walks through known GC heaps checking for whether the specified interior pointer falls within the range of the known GC object allocations. Obviously, this method is not readily accessible from end-user managed code and, for all I know, may not even be relevant if there's no GC in progress.

I also looked through the new Span<T> and System.Memory libraries, but couldn't find a sequence of manipulations that would recover an (e.g.) array handle if you didn't start by providing it in the first place (whereby the containing handle gets squirreled away in those various structs). In cases where the _pinnable is optional (e.g. Span<T>), the GC handle in the struct is null, so if you don't opt-in from the start, there's no way to get it back.

Summary: Is there any way to recover the containing object handle from a managed pointer?

[edit:] If the managed pointer points to a value-type on the stack, it would be perfectly reasonable for the (putative) handle recovery function to indicate failure by (e.g.) returning 'null'.


Related: How does the C# garbage collector find objects whose only reference is an interior pointer?


Solution

  • No, recovery of the containing object from an interior pointer is not possible. During GC, interior pointers are translated into corresponding objects thanks to the so-called brick table and plug trees. Given a specified address, the proper brick table entry is calculated and corresponding plug tree is traversed to find the plug within which that address lives. Finally, that plug is scanned object-by-object to find the one that contains the considered address.

    The point is those trees are built and available only during GC. So, even if such an "interior pointer recovery" API existed, it would have to wait for the GC and could provide an answer only afterwards (which seems very impractical). Other solutions, like linear memory scanning, would obviously possibly introduce tremendous overhead.