Managed/Unmanaged: pinning ptr madness
-
Hi! Short form: Imagine two managed functions
void Class::Function1(SomeValueType val1) void Class::Function2(SomeValueType * val2)
And an unmanaged function:void Foo(void * pThis, void * pSomeValueType);
pThis shall receive the this-pointer of Class , pSomeValueClass shall receive a pointer to the SomeValueType instance. This, of course works:void Class::Function1(SomeValueType val1) { Class __pin * pinnedThis = this; SomeValueType __pin * pinnedVal = &val1; Foo(reinterpret_cast(pinnedThis), reinterpret_cast(pinnedVal)); } void Class::Function2(SomeValueType * val2) { Class __pin * pinnedThis = this; SomeValueType __pin * pinnedVal = val2; Foo(reinterpret_cast(pinnedThis), reinterpret_cast(pinnedVal)); }
However, this works as well:void Class::Function1(SomeValueType val1) { Foo(reinterpret_cast(&(*this)), reinterpret_cast(&val1)); }
How can it be, that &(*this) and &val1 are implicitely are converted to __nogc pointers that can be castet to void*? Thanks for your help! Long form (including history and research): Please take a view moments to read my post. I am quite new to MC++ and .net, and so the following questions might sound rediculous: I am working on a managed classlibrary that wraps a small part of Direct3D8. I know, Managed DX is out, however that is not what I need. The classlib calls native functions. E.g. one function (it is one from Direct3DX) has the following signature: D3DXMATRIX * D3DXMatrixMultiply(D3DXMATRIX * out, D3DXMATRIX * mat1, D3DXMATRIX * mat2); First question: what is the better method: using DllImportAttribute to import the method, or include the appropriate header file () and lib ("D3DX8.lib")? Speed is more important than size. So, now I have a __value class Matrix, which has the same memory layout as D3DXMATRIX.public __value class Matrix { public: float M11, M12, M13, M14; float M21, M22, M23, M24; float M31, M32, M33, M34; float M41, M42, M43, M44; public: void Multiply(Matrix * src); };
Matrix::Multiply shall use D3DXMatrixMultiply. So naivly I tried this:void Matrix::Multiply(Matrix * src) { D3DXMatrixMultiply(this, this, src); }
OK, stupid me, the compiler complains that it cannot convert Matrix __gc *const to D3DXMATRIX. Seems logical. As a good citizen I use __pin pointers: Second try -
Hi! Short form: Imagine two managed functions
void Class::Function1(SomeValueType val1) void Class::Function2(SomeValueType * val2)
And an unmanaged function:void Foo(void * pThis, void * pSomeValueType);
pThis shall receive the this-pointer of Class , pSomeValueClass shall receive a pointer to the SomeValueType instance. This, of course works:void Class::Function1(SomeValueType val1) { Class __pin * pinnedThis = this; SomeValueType __pin * pinnedVal = &val1; Foo(reinterpret_cast(pinnedThis), reinterpret_cast(pinnedVal)); } void Class::Function2(SomeValueType * val2) { Class __pin * pinnedThis = this; SomeValueType __pin * pinnedVal = val2; Foo(reinterpret_cast(pinnedThis), reinterpret_cast(pinnedVal)); }
However, this works as well:void Class::Function1(SomeValueType val1) { Foo(reinterpret_cast(&(*this)), reinterpret_cast(&val1)); }
How can it be, that &(*this) and &val1 are implicitely are converted to __nogc pointers that can be castet to void*? Thanks for your help! Long form (including history and research): Please take a view moments to read my post. I am quite new to MC++ and .net, and so the following questions might sound rediculous: I am working on a managed classlibrary that wraps a small part of Direct3D8. I know, Managed DX is out, however that is not what I need. The classlib calls native functions. E.g. one function (it is one from Direct3DX) has the following signature: D3DXMATRIX * D3DXMatrixMultiply(D3DXMATRIX * out, D3DXMATRIX * mat1, D3DXMATRIX * mat2); First question: what is the better method: using DllImportAttribute to import the method, or include the appropriate header file () and lib ("D3DX8.lib")? Speed is more important than size. So, now I have a __value class Matrix, which has the same memory layout as D3DXMATRIX.public __value class Matrix { public: float M11, M12, M13, M14; float M21, M22, M23, M24; float M31, M32, M33, M34; float M41, M42, M43, M44; public: void Multiply(Matrix * src); };
Matrix::Multiply shall use D3DXMatrixMultiply. So naivly I tried this:void Matrix::Multiply(Matrix * src) { D3DXMatrixMultiply(this, this, src); }
OK, stupid me, the compiler complains that it cannot convert Matrix __gc *const to D3DXMATRIX. Seems logical. As a good citizen I use __pin pointers: Second tryVizOne wrote: How can it be, that &(*this) and &val1 are implicitely are converted to __nogc pointers that can be castet to void*? Essentially, __gc pointer and a __nogc pointer are the same thing, just values that hold memory addresses, whether pinned or not. So that you got away without pinning first, is just luck that the GC memory where the pointed-to objects were stored, was not moved during a garbage collection cycle. Pinning mostly just tells the GC not to move any object pointed-to by a pinned pointer. If the GC moved the objects before the unpinned version of Function1() was called, there would be serious problems. However, there also is no guarantee that the GC would actually move those particular objects during collection; it is simply a possibility. Incidentally, there are a few implicit things that occur in MC++ that are not well documented. Also, I recall reading somewhere that because of such things, any use of reinterpret_cast is discouraged in MC++. Of course, what would we do without it? MS is likely just covering their "tracks" with such warnings ;) Sorry, but I had trouble following your longer explanation, partly because the angle-bracketted portions of the casts were stripped (I sometimes forget to turn off HTML interpretation when I post too - very annoying, isn't it?), I am not too familiar with DirectX, and I could not see the IL for myself. However, I very much appreciate the extended explanation; it's short explanations that I more often have trouble interpreting. Cheers
-
VizOne wrote: How can it be, that &(*this) and &val1 are implicitely are converted to __nogc pointers that can be castet to void*? Essentially, __gc pointer and a __nogc pointer are the same thing, just values that hold memory addresses, whether pinned or not. So that you got away without pinning first, is just luck that the GC memory where the pointed-to objects were stored, was not moved during a garbage collection cycle. Pinning mostly just tells the GC not to move any object pointed-to by a pinned pointer. If the GC moved the objects before the unpinned version of Function1() was called, there would be serious problems. However, there also is no guarantee that the GC would actually move those particular objects during collection; it is simply a possibility. Incidentally, there are a few implicit things that occur in MC++ that are not well documented. Also, I recall reading somewhere that because of such things, any use of reinterpret_cast is discouraged in MC++. Of course, what would we do without it? MS is likely just covering their "tracks" with such warnings ;) Sorry, but I had trouble following your longer explanation, partly because the angle-bracketted portions of the casts were stripped (I sometimes forget to turn off HTML interpretation when I post too - very annoying, isn't it?), I am not too familiar with DirectX, and I could not see the IL for myself. However, I very much appreciate the extended explanation; it's short explanations that I more often have trouble interpreting. Cheers
Thank you for your answer. It is obvious that passing a cast (non-pin) pointer to a native function is dangerous for heap objects, as the GC may compact the heap, move objects etc. But what about __value object? They are stored on stack, aren't they? Will stack object be move during collection, too? How heavy would you say is pinning a pointer? Is it a real performance issue if I keep the pointer pinned only for some native calculations that won't last more then a view microseconds? - Andre
-
Thank you for your answer. It is obvious that passing a cast (non-pin) pointer to a native function is dangerous for heap objects, as the GC may compact the heap, move objects etc. But what about __value object? They are stored on stack, aren't they? Will stack object be move during collection, too? How heavy would you say is pinning a pointer? Is it a real performance issue if I keep the pointer pinned only for some native calculations that won't last more then a view microseconds? - Andre
Me again! Funnily, during a test I found out, that pinning the pointer first and then passing it to the native function works slightly faster... Of course reinterpret_cast is an evil monster, but I have to use it here, haven't I? Let's say, I have those structs:
[StructLayout(LayoutKind::Sequential, Pack=4)] public __value struct ManagedStruct { float AddComponents(); float a, b, c; }; #pragma pack(push, 4) struct NativeStruct { float a, b, c; } #pragma pack(pop)
I guess these structs are guaranteed to be binarilly identical. Now I have a function:// the native version #pragma unmanaged float AddComponents(NativeStruct * p) { return p->a + p->b + p->c; } #pragma
If I want to call this from within managed code via IJW, I'd do this:float ManagedStruct::AddComponents() { ManagedStruct __pin * pinThis = this; return ::AddComponents(reinterpret_cast(pThis));
The reinterpret_cast is safe and ok, or not? Is there another way of calling the native function? - Andre -
Me again! Funnily, during a test I found out, that pinning the pointer first and then passing it to the native function works slightly faster... Of course reinterpret_cast is an evil monster, but I have to use it here, haven't I? Let's say, I have those structs:
[StructLayout(LayoutKind::Sequential, Pack=4)] public __value struct ManagedStruct { float AddComponents(); float a, b, c; }; #pragma pack(push, 4) struct NativeStruct { float a, b, c; } #pragma pack(pop)
I guess these structs are guaranteed to be binarilly identical. Now I have a function:// the native version #pragma unmanaged float AddComponents(NativeStruct * p) { return p->a + p->b + p->c; } #pragma
If I want to call this from within managed code via IJW, I'd do this:float ManagedStruct::AddComponents() { ManagedStruct __pin * pinThis = this; return ::AddComponents(reinterpret_cast(pThis));
The reinterpret_cast is safe and ok, or not? Is there another way of calling the native function? - AndreHi VizOne, Sorry for the late reply. VizOne wrote: But what about __value object? They are stored on stack, aren't they? Will stack object be move during collection, too? Although __value objects are typically stored on the stack, they can also be put on the heap. Stack objects are the same in .Net as they are in regular C/C++, so they won't move. How heavy would you say is pinning a pointer? Is it a real performance issue if I keep the pointer pinned only for some native calculations that won't last more then a view microseconds? I did some preliminary tests too, and I could barely measure any performance differences. I forced GC collections during them. The GC does not collect very often under normal circumstances, so I don't see pinning as costly either. Your tests jibe with mine. Of course reinterpret_cast is an evil monster, but I have to use it here, haven't I? I always do, and MC++ code that ships with .Net uses it too. I really think MS is just playing it "safe". As I recall, reinterpret_cast was just discouraged. The software world would grind to a halt if such casts were never used ;) The reinterpret_cast is safe and ok, or not? I usually call native functions from MC++ much like you do. As long as managed objects are pinned and the cast is appropriate, safety should not be of concern. Cheers