Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need to pin a struct when copying bytes from the memory location

I have defined a struct in C# to mirror a native data structure and used the StructLayout of Sequential. To transform the struct to the 12 bytes (3x 4 bytes) required by the Socket IOControl method, I am using Marshal.Copy to copy the bytes to an array.

As the struct only contains value types, do I need to pin the structure before I perform the copy? I know the GC compacts the heap and therefore the mem address of reference types can change during a GC. Is the same the case for stack allocated value types?

The current version which includes the pin operation looks like:

[StructLayout(LayoutKind.Sequential, Pack = 1)]  
struct TcpKeepAliveConfiguration
{
        public uint DoUseTcpKeepAlives;
        public uint IdleTimeMilliseconds;
        public uint KeepAlivePacketInterval;

        public byte[] ToByteArray()
        {
            byte[] bytes = new byte[Marshal.SizeOf(typeof(TcpKeepAliveConfiguration))];
            GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
            try
            {
                Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
                return bytes;
            }
            finally
            {
                pinStructure.Free();
            }
        }
    }

Any thoughts?

like image 508
BrianB Avatar asked Apr 19 '11 10:04

BrianB


2 Answers

If your structure is captured by, say, a lambda expression, it won't be stored on the stack.

Thus, I'd recommend you always pin the structure before copying.

Eric Lippert wrote an article about value type storage that might interest you.

like image 185
Frédéric Hamidi Avatar answered Sep 21 '22 03:09

Frédéric Hamidi


Frédéric and Aliostad are correct; you do not know where the "this" actually lives, and therefore you don't know whether the garbage collector is allowed to move it or not.

I just want to point out that there is an equivalent solution to your problem that you might find useful. You can also solve your problem with:

public byte[] ToByteArray()
{
  byte[] bytes = new byte[Marshal.SizeOf(typeof(TcpKeepAliveConfiguration))];
  unsafe
  {
    fixed (TcpKeepAliveConfiguration* ptr = &this)
    {
      // now you have pinned "this" and obtained a pointer to it in one step
    }
  }
}

The "fixed" statement ensures that during the body of its block, the unmanaged pointer to "this" is valid because the memory cannot be moved by the garbage collector. Basically it is another way of writing your code; some people find this way a bit easier to read.

(Note that you have to check the "allow unsafe" checkbox in Visual Studio or use the "/unsafe" flag on the command line when you are building code that contains an unsafe context.)

like image 21
Eric Lippert Avatar answered Sep 20 '22 03:09

Eric Lippert