Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Pinning in Managed C++


September 2002/Pinning in Managed C++

Pinning refers to the process of informing the CLR’s (Common Language Runtime) GC (Garbage Collector) that it is not to relocate a specified object in memory.

Pinning is necessary in certain scenarios where it is necessary to pass managed data to unmanaged code, or potentially in code that is attempting to eke out the highest possible performance.

Consider a scenario in which a managed array of integers is passed by reference to an unmanaged function for manipulation, sans pinning. Unaware that there is now an unmanaged pointer to this data, the GC could decide to compact its heap, moving the original data, and leaving the unmanaged code poised to access now indeterminate memory.

Different managed languages provide varying levels of support for pinning. Visual Basic provides no means to explicitly pin an object. C# provides in the language a “fixed” statement, which enables an object to be explicitly pinned in an unsafe context (enabling the use of pointers, in that language). Managed C++ provides similar explicit pinning capability with its __pin keyword.

Additionally, the .NET Framework’s P/Invoke (Platform Invoke) technology automatically pins many types of arguments that are passed to an unmanaged API, regardless of language used. C++ developers can rely on this mechanism, or choose to use the higher performing (and easier to use) IJW (It Just Works) mechanism for calling unmanaged code. In the case of IJW, however, it is the developer’s responsibility to manually pin any managed data being passed.

Finally, any .NET consumer language, regardless of whether or not it provides intrinsic language support for pinning, can pin some types of objects by calling the static Alloc() method of the .NET Framework’s GCHandle class, as shown by this MC++ code:

// a simple .NET string, pinned
// manually by GCHandle::Alloc()
using namespace System::Runtime::InteropServices;
...

String* s = S"Hello, World";
GCHandle gch = GCHandle::Alloc(s,
  GCHandleType::Pinned);

// here we gain access to the string's 
// buffer
wchar_t* ps = reinterpret_cast<wchar_t*>
  (gch.AddrOfPinnedObject().ToInt32());

...

// and, we're responsible for releasing 
// the pin
gch.Free();

I’ll cover more on this technique in a moment.

Explicit pinning in Managed C++ is different than explicit pinning in C#. In C# the language provides the “fixed” construct, which accepts an object to be pinned for the scope of the statement. Nested fixed statements are necessary to pin multiple objects:

int[] arr_int = new int[10];
double[] arr_double = new double[10];

unsafe
{
fixed (int* pi = &arr_int[5])
{
  fixed (double* pd = &arr_double[5])
  {
    *pi = 5;
    *pd = 10.0;
  }
}
}

In Managed C++, the __pin keyword modifies a pointer type, creating a type called a “pinning pointer.” It is used to specify that a pointer to an instance of an object is to be pinned:

Int32 arr_int[] = new Int32[10];
Double arr_double[] = new Double[10];

int __pin* pi = &arr_int[5];
double __pin* pd = &arr_double[5];

*pi = 5;
*pd = 10;

The pinning remains in effect as long as the declared variable remains in scope. It’s interesting to note that this behavior is actually achieved because the compiler will inject code at the end of the scope to nullify the pinning variable, causing the object to unpin. This is because IL (Intermediate Language) has no concept of block scope.

(In each of the above examples, it is important to note that while you are declaratively pinning the fifth element of the arrays, the GC is in actuality pinning the entire array. And, when pinning a member of a managed object, the GC will pin the whole object.)

While __pin is somewhat more flexible than fixed, it does have certain limitations. __pin can only be used in a local context (a member of a class cannot be a pinning pointer), and __pin cannot be used in cast expressions, requiring the developer to pre-declare a pinning pointer variable before referencing it.

Specifically, Microsoft documentation identifies the following characteristics and constraints of pinning pointers:

  • Except for its pinning properties, a pinning pointer is identical to a __gc pointer.
  • A pinning pointer can be implicitly converted to a __nogc pointer. (Note this is done in the previous examples.)
  • A pinning variable shall be a non-static local variable.
  • Two function overloads shall not differ only by the use of pin and __gc pointer types.
  • A pinning pointer type shall not be used in a cast expression. It can only be used to declare a variable.

Earlier I briefly showed how it is possible to call GCHandle::Alloc() to pin an object, without relying on language constructs. This technique is interesting because it behaves somewhat differently than __pin (or fixed) and has its own (differing) set of limitations.

The first major difference is that GCHandle::Alloc(..., GCHandleType::Pinned) will only pin certain types. Specifically, it will pin Strings, Arrays, and any type that is deemed “blittable.” Blittable types are those that have a static, well-defined memory layout that can be copied directly while being marshaled across managed and unmanaged code boundaries. Blittable types intrinsic to the CLR include the core integral types. User-defined value and reference types can be made blittable by declaring them with the StructLayout attribute, specifying a LayoutKind of either Sequential or Explicit. For value and reference types that weren’t defined with StructLayout, there’s nothing that can be done to enable their pinning by GCHandle::Alloc().

An InvalidArgument exception will be raised if a non-blittable type is passed to GCHandle::Alloc() for pinning.

Conversely, __pin will pin any type, blittable or not.

A second major behavior difference is that the pointer resolved by using __pin to pin an instance of a String will point to a different location than the pointer retrieved by GCHandle::AddrOfPinnedObject(). How can this be?

For Strings and Arrays, GCHandle::AddrOfPinnedObject() is hard coded to calculate and return an interior pointer to the data buffer of the string or array it is acting upon. __pin simply returns the base address of the String or Array object.

For other (blittable) types, GCHandle::AddrOfPinnedObject() will calculate and return a pointer to the first data member of the type. Again, __pin returns the base address of the entire pinned object.

This behavior is actually desirable in many cases. In the case of a String, or other non-blittable types, you are not privy to the internal layout of the object. So you would not know how to access any data in particular once you’d pinned an instance with __pin. (Actually, this is not 100 percent true. The framework does provide a value that is the offset into a string to its internal data buffer.) And for blittable types, you are not typically interested in the layout of the type beyond what is the known blittable portion.

The two pinning techniques do differ in performance. __pin prompts the compiler to emit a single attribute in MSIL (Microsoft Intermediate Language) specifying to the CLR and the GC to pin the denoted object. GCHandle adds the overhead of type checking, pointer arithmetic, and several function calls.

Manually calling the GCHandle methods to perform pinning would normally be more cumbersome than using __pin, but it is pretty simple to create a C++ template class that will use GCHandle to enable inline pinning, like this:

String* s = S"Hello, World.";
SomeUnmanagedFunction
  ( pin_cast<const wchar_t*>(s) );

The pin_cast<> struct (see Listing 1) is modeled after the gcroot<> class provided with Visual C++ .NET. (gcroot<> enables unmanaged classes to hold pointers to managed objects.) When instantiated inline, the constructor pins the passed object by calling GCHandle::Alloc(), storing the newly initialized GCHandle as a member of the class. The function-call operator calls GCHandle::AddrOfPinnedObject(), retrieves the pinned address (which will point to the data buffer of a string or array, or the first data member of a blittable type), and returns it, casting it to the type specified in the template parameter. The destructor, of course, calls GCHandle::Free() to release the pin. All very tidy.

One last important point: in some of these examples, I’m showing how pin_cast can be used to access the private data buffer of managed Strings and Arrays, possibly in a non-const manner. Given these are sealed types, and that the entire implementation is unknown, it would be bad to assume you could safely modify the contents of these buffers, even if the memory is pinned.

At first glance, Visual C++ with Managed Extensions may appear bizarre or perhaps even an oddity along side the other managed .NET languages. The keywords added to the language, to expose and enable CLR concepts (such as pinning) within C++ code, do require an understanding of their specific purpose and syntax. But Managed C++ is just as capable as C# and Visual Basic and even offers advanced developers more opportunity for better performance and increased flexibility in code, all without abandoning their favorite language.

References

[1] <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/html/vcManagedExtensionsSpec_7_7.asp>

[2] <http://msdn.microsoft.com/library/en-us/vcmex/html/ vclrf__pin.asp?frame=true>

About the Author

Nick Hodapp is Microsoft’s product manager for Visual C++ .NET, a developer tool for creating best-in-class applications for Windows and .NET using the C++ language. Previously Nick enjoyed an eight-year career in the Midwest developing Windows-based GIS software for telecommunication and power utilities.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.