Note that there are some explanatory texts on larger screens.

plurals
  1. PO.NET C# unsafe/fixed doesn't pin passthrough array element?
    primarykey
    data
    text
    <p>I have some concurrent code which has an intermittent failure and I've reduced the problem down to two cases which seem identical, but where one fails and the other doesn't.</p> <p>I've now spent way too much time trying to create a minimal, complete example that fails, but without success, so I'm just posting the lines that fail in case anyone can see an obvious problem. </p> <pre><code>Object lock = new Object(); struct MyValueType { readonly public int i1, i2; }; class Node { public MyValueType x; public int y; public Node z; }; volatile Node[] m_rg = new Node[300]; unsafe void Foo() { Node[] temp; while (true) { temp = m_rg; /* ... */ Monitor.Enter(lock); if (temp == m_rg) break; Monitor.Exit(lock); } #if OK // this works: Node cur = temp[33]; fixed (MyValueType* pe = &amp;cur.x) *(long*)pe = *(long*)&amp;e; #else // this reliably causes random corruption: fixed (MyValueType* pe = &amp;temp[33].x) *(long*)pe = *(long*)&amp;e; #endif Monitor.Exit(lock); } </code></pre> <p>I have studied the IL code and it looks like what's happening is that the Node object at array position 33 is moving (in very rare cases) despite the fact that we are holding a pointer to a value type within it.</p> <p>It's as if the CLR doesn't notice that we are passing <em>through</em> a heap (movable) object--the array element--in order to access the value type. The 'OK' version has never failed under extended testing on an 8-way machine, but the alternate path fails quickly every time.</p> <ul> <li> Is this never supposed to work, and 'OK' version is too streamlined to fail under stress?</li> <li>Do I need to pin the object myself using GCHandle (I notice in the IL that the <i>fixed</i> statement alone is not doing so)?</li> <li>If manual pinning is required here, why is the compiler allowing access through a heap object (without pinning) in this way?</li> </ul> <p><em>note: This question is not discussing the elegance of reinterpreting the blittable value type in a nasty way, so please, no criticism of this aspect of the code unless it is directly relevant to the problem at hand.. thanks</em></p> <p>[edit: jitted asm] Thanks to Hans' reply, I understand better why the jitter is placing things on the stack in what otherwise seem like vacuous asm operations. See [rsp + 50h] for example, and how it gets nulled out after the 'fixed' region. The remaining unresolved question is whether [cur+18h] (lines 207-20C) on the stack is somehow sufficient to protect the access to the value type in a way that is <em>not</em> adequate for [temp+33*IntPtr.Size+18h] (line 24A).</p> <p><img src="https://i.stack.imgur.com/c7T3y.png" alt="enter image description here"></p> <p>[edit]</p> <h3>summary of conclusions, minimal example</h3> <p>Comparing the two code fragments below, I now believe that #1 is not ok, whereas #2 is acceptable.</p> <p>(1.) The following <strong>fails</strong> (on x64 jit at least); GC can still move the <strong>MyClass</strong> instance if you try to fix it <em>in-situ</em>, via an array reference. There's no place on the stack for the reference of the particular object instance (the array element that needs to be fixed) to be published, for the GC to notice.</p> <pre><code>struct MyValueType { public int foo; }; class MyClass { public MyValueType mvt; }; MyClass[] rgo = new MyClass[2000]; fixed (MyValueType* pvt = &amp;rgo[1234].mvt) *(int*)pvt = 1234; </code></pre> <p>(2.) But you <strong>can</strong> access a structure inside a (movable) object using <em>fixed</em> (without pinning) if you provide an explicit reference on the stack which can be advertised to the GC:</p> <pre><code>struct MyValueType { public int foo; }; class MyClass { public MyValueType mvt; }; MyClass[] rgo = new MyClass[2000]; MyClass mc = &amp;rgo[1234]; // &lt;-- only difference -- add this line fixed (MyValueType* pvt = &amp;mc.mvt) // &lt;-- and adjust accordingly here *(int*)pvt = 1234; </code></pre> <p>This is where I'll leave it unless someone can provide corrections or more information...</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload