Note that there are some explanatory texts on larger screens.

plurals
  1. POReordering of operations around volatile
    primarykey
    data
    text
    <p>I'm currently looking at a copy-on-write set implementation and want to confirm it's thread safe. I'm fairly sure the only way it might not be is if the compiler is allowed to reorder statements within certain methods. For example, the <code>Remove</code> method looks like:</p> <pre><code>public bool Remove(T item) { var newHashSet = new HashSet&lt;T&gt;(hashSet); var removed = newHashSet.Remove(item); hashSet = newHashSet; return removed; } </code></pre> <p>Where hashSet is defined as</p> <pre><code>private volatile HashSet&lt;T&gt; hashSet; </code></pre> <p>So my question is, given that hashSet is <code>volatile</code> does it mean that the <code>Remove</code> on the new set happens before the write to the member variable? If not, then other threads may see the set before the remove has occurred.</p> <p>I haven't actually seen any issues with this in production, but I just want to confirm it is guaranteed to be safe.</p> <p><strong>UPDATE</strong></p> <p>To be more specific, there's another method to get an <code>IEnumerator</code>:</p> <pre><code>public IEnumerator&lt;T&gt; GetEnumerator() { return hashSet.GetEnumerator(); } </code></pre> <p>So the more specific question is: is there a guarantee that the returned <code>IEnumerator</code> will never throw a <code>ConcurrentModificationException</code> from a remove?</p> <p><strong>UPDATE 2</strong></p> <p>Sorry, the answers are all addressing the thread safety from multiple writers. Good points are raised, but that's not what I'm trying to find out here. I'd like to know if the compiler is allowed to re-order the operations in <code>Remove</code> to something like this:</p> <pre><code> var newHashSet = new HashSet&lt;T&gt;(hashSet); hashSet = newHashSet; // swapped var removed = newHashSet.Remove(item); // swapped return removed; </code></pre> <p><em>If</em> this was possible, it would mean that a thread could call <code>GetEnumerator</code> after <code>hashSet</code> had been assigned, but before <code>item</code> was removed, which could lead to the collection being modified during enumeration.</p> <p>Joe Duffy has a <a href="http://www.bluebytesoftware.com/blog/2008/06/13/VolatileReadsAndWritesAndTimeliness.aspx" rel="nofollow">blog article</a> that states:</p> <blockquote> <p>Volatile on loads means ACQUIRE, no more, no less. (There are additional compiler optimization restrictions, of course, like not allowing hoisting outside of loops, but let’s focus on the MM aspects for now.) The standard definition of ACQUIRE is that subsequent memory operations may not move before the ACQUIRE instruction; e.g. given { ld.acq X, ld Y }, the ld Y cannot occur before ld.acq X. However, previous memory operations can certainly move after it; e.g. given { ld X, ld.acq Y }, the ld.acq Y can indeed occur before the ld X. The only processor Microsoft .NET code currently runs on for which this actually occurs is IA64, but this is a notable area where CLR’s MM is weaker than most machines. Next, all stores on .NET are RELEASE (regardless of volatile, i.e. volatile is a no-op in terms of jitted code). The standard definition of RELEASE is that previous memory operations may not move after a RELEASE operation; e.g. given { st X, st.rel Y }, the st.rel Y cannot occur before st X. However, subsequent memory operations can indeed move before it; e.g. given { st.rel X, ld Y }, the ld Y can move before st.rel X.</p> </blockquote> <p>The way I read this is that the call to <code>newHashSet.Remove</code> requires a <code>ld newHashSet</code> and the write to <code>hashSet</code> requires a <code>st.rel newHashSet</code>. From the above definition of RELEASE no loads can move after the store RELEASE, so <em>the statements cannot be reordered</em>! Could someone confirm please confirm my interpretation is correct?</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.
    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