none
Do I need to pin? RRS feed

  • Question

  • I have unmanaged funciton:

    int fu1(char** buf);

    I use following in C#:

    static extern int fu1(ref IntPtr buf);

    and I call it like:

    byte[] mBuf;
    IntPtr temp = IntPtr();
    int size = fu1(ref temp);
    mDuf = new byte[size];
    Marshal.Copy(temp, mBuf, 0, size);

    This works fine, but the question is - do I need to pin temp?

    Tuesday, March 16, 2010 10:10 PM

All replies

  • No.  Since temp is an IntPtr, you're just passing around a memory address, which is (presumably) being set by unmanaged code.

    There is no need to pin in this case.


    However, in the code you're showing, nothing ever free's the memory being pointed to by temp.... You'll need another unmanaged method to release this memory, or you'll have a leak.


    Reed Copsey, Jr. - http://reedcopsey.com
    Tuesday, March 16, 2010 11:13 PM
    Moderator
  • 1. I do free temp later. Sorry I forgot to copy this part into the sample.
    2. I am not sure I understand your response completely. Memory is not "set" by unmanaged code - I actually pass it pointer and unmanaged application allocates memory and fill the context. Question is - whether garbage collector can move it!
    Wednesday, March 17, 2010 9:53 PM
  • Your unmanaged code is allocating memory, but also setting the IntPtr (temp) to the location of the newly allocated memory.

    The GC will not free this memory - the GC in the CLR only knows about memory it allocated.  If you allocate memory in a native function, then the GC will not move it.

    Also, IntPtr is not like a managed pointer - it's an actual memory address, either a 32bit or 64bit integer.  You do not have to worry about the GC trying to move this memory, since it is not part of the managed heap.



    Reed Copsey, Jr. - http://reedcopsey.com
    Wednesday, March 17, 2010 10:18 PM
    Moderator
  • 1. I do free temp later. Sorry I forgot to copy this part into the sample.
    2. I am not sure I understand your response completely. Memory is not "set" by unmanaged code - I actually pass it pointer and unmanaged application allocates memory and fill the context. Question is - whether garbage collector can move it!

    When you write "whether garbage collector can move it" what are you referring to when you say "it"?

    The marshal copy operaion will do a pin itself if it needs to, the IntPtr is a non-embedded value type and so that is a local stack variable and the GC won't touch it.

    The only thing in your code that the GC would "see" is the managed array, and that is pinned (if needed) by the marshal copy operation.

    In my view, C# will insist on you pinning if pinning is necessary (it will insist on using "fixed" keyword). I dont think you can write code that requires a pinning operation ("fixed") that will compile, the compiler (I think) always tells you that you need to pin, I think this is true and so many coders worry about it unnecesarily.

    H
    Thursday, March 18, 2010 3:55 PM

  • In my view, C# will insist on you pinning if pinning is necessary (it will insist on using "fixed" keyword). I dont think you can write code that requires a pinning operation ("fixed") that will compile, the compiler (I think) always tells you that you need to pin, I think this is true and so many coders worry about it unnecesarily.

    H
    This is not quite true.  You need to pin if you pass managed memory to an unmanaged API, and the unmanaged API keeps a reference to the memory location after it returns.

    In this situation, the unmanaged API expects that memory to exist at a specific location, but the marhsaller has returned, and the GC believes it can move the memory if it wants.  Without pinning, the next time you call into unmanaged code, or the unmanaged code tries to use that memory, you'll have a problem.

    Reed Copsey, Jr. - http://reedcopsey.com
    Thursday, March 18, 2010 5:04 PM
    Moderator

  • In my view, C# will insist on you pinning if pinning is necessary (it will insist on using "fixed" keyword). I dont think you can write code that requires a pinning operation ("fixed") that will compile, the compiler (I think) always tells you that you need to pin, I think this is true and so many coders worry about it unnecesarily.

    H
    This is not quite true.  You need to pin if you pass managed memory to an unmanaged API, and the unmanaged API keeps a reference to the memory location after it returns.

    In this situation, the unmanaged API expects that memory to exist at a specific location, but the marhsaller has returned, and the GC believes it can move the memory if it wants.  Without pinning, the next time you call into unmanaged code, or the unmanaged code tries to use that memory, you'll have a problem.

    Reed Copsey, Jr. - http://reedcopsey.com


    Actually yes I agree, thanks for pointing that out. This is when one would use the GCHandle class to pin. Of course that is only possible if the managed object being pinned is itself deemed marshalable (it will raise an exception otherwise).

    I have asked before, is there a "blunt" API or anything that one can use to "pin" anything one likes, but it seems there isn't I guess the act of pinning really calls into the GC machinery itself and just flags some metadata in the managed object's header or something.

     

    Cap'n

     

     

     

    Friday, March 19, 2010 4:29 PM
  • Reed has done a great job of answering the question, but it isn't clear that you have "got it". Maybe I can illustrate the answer in different terms:

    int fu1(char** buf)

    Is a method that accepts as its parameter the address of a location. That location must itself be a pointer-sized storage location (char*). We'll refer to this thing as 'bufLoc'. The method is going to allocate a block of (unmanaged) memory and store the address of that memory in 'bufLoc'. It can do that because it has the address of 'bufLoc'.

    On the managed side, this is what happens:

    // Declare a pointer-sized variable (on the stack, but its location is not relevant)
    IntPtr bufLoc;
    // Call fu1, passing the address of that variable (that's what 'ref' means, to pass the address of)
    fu1(ref bufLoc);

    When fu1 returns, bufLoc will contain the address of the memory fu1 allocated. That is, bufLoc is a pointer to a block of unmanaged memory.

    When you call Marshal.Copy you are doing exactly that: Copying the entire block of unmanaged memory into managed memory (which you allocated). Anything you do with mBuf, you do in the managed world. That's why you made the copy. You can't operate on the unmanaged block of memory with managed code (you can, but you have to switch to unsafe mode and use pointers), so you copied it into managed memory.

    Friday, March 19, 2010 6:09 PM
  • I made a little blunder when I said "on the stack, but its location is not relevant". It is quite relevant.

    Any time you give unmanaged code an address, you are giving that code free reign to modify bytes at that location. In this case IntPtr bufLoc is a block of memory on the stack (4 bytes in a 32-bit process). The managed stack is always 'pinned', so there is no problem doing so (It is still technically 'unsafe', but falls closer to the safe end of the spectrum in this particular scenario).

    The problem with passing an address of something on the managed heap is that the GC is free to move memory around when it performs its work. The GC is unaware of the fact that you have given out an address to unmanaged code, so if you do that, you need to 'pin' the memory. This informs the GC that it cannot move that memory (for the duration of the pin). Now you may be thinking, "What does it matter, the unmanaged function is going to do its thing and return." But the GC runs in its own thread. It's entirely possible for managed memory to get moved around, even in the middle of a method call.

     

    Friday, March 19, 2010 6:22 PM
  • Hi olelukoyya ,

    I'm writing to check the issue status, please feel free to let us know if you have any concern.


    Sincerely,
    Eric
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Wednesday, March 24, 2010 5:28 AM
  • In my view, C# will insist on you pinning if pinning is necessary (it will insist on using "fixed" keyword). I dont think you can write code that requires a pinning operation ("fixed") that will compile, the compiler (I think) always tells you that you need to pin

    This is not quite true.  You need to pin if you pass managed memory to an unmanaged API, and the unmanaged API keeps a reference to the memory location after it returns.

    There appear to be some other edge cases which may be cause for concern. A discussion here seems to suggest that code such as the following must be manually pinned, yet the compiler does not complain:

    struct MyValueType { public int foo; };
    class MyClass { public MyValueType mvt; };
    
    fixed (MyValueType* pvt = &rgo[1234].mvt)
     *(int*)pvt = 1234; // fails; GC moves the MyClass instance
    
    


    I found that changing it to the following (still with no manual pinning), has never failed (yet) under heavy multithreaded stress (gcServer, x64, .NET 4.0), but the jury is still out on whether manual pinning is absolutely necessary:


    MyClass mc = &rgo[1234];
    fixed (MyValueType* pvt = &mc.mvt)
     *(int*)pvt = 1234; // Seems to be OK
    
    
    • Edited by Glenn Slayden Friday, April 8, 2011 6:27 PM fix 2nd example
    Friday, April 8, 2011 9:39 AM