Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to mix locks and interlock operations?

I have some code which must be thread safe whose behavior resembles this:


protected long m_RunningValue
protected long m_RunningCounter
protected object m_Lock = new object();

public long RunningValue { get { return Interlocked.Read(m_RunningValue); } }
public long RunningCounter { get { return Interlocked.Read(m_RunningCounter); } }

public void DoCalculation(int newValue, int newQuantity)
{
   lock(m_Lock)
   {
       Interlocked.Add(ref m_RunningValueA, newValue);
       Interlocked.Add(ref m_RunningCounter, newQuantity);
       if(Interlocked.Read(ref newQuantity) == 0)
       { 
         ...m_RunningValue gets further modified here
       }
   }
}

The Calculation must lock both the value and the counter or a race condition could effect the if(...) block, however they do not need to be synchronized at all when being read out, i.e. if the counter and value changes between attempts to read both, that's 100% ok with me.

The Interlock on the read is there for thread-safety reading of a 64-bit value.

  1. Is mixing interlocks and locks like this safe? I have read on other web pages that mixing them is unsafe, however I can't find clarification if this means that mixing them is a great way to introduce subtle bugs, or if at a system level this can corrupt the data structures involved.

  2. Is the cost of all this interlocking (64-bit .NET 4.0 runtime) completely defeating the purpose of saving a ReaderWriterSlim lock around the property get() methods?

like image 902
Chuu Avatar asked Dec 07 '10 23:12

Chuu


1 Answers

With regards to the edited version, this is not guaranteed to be thread-safe.

Consider a 32-bit platform. This means that 64-bit long values must be accessed in two separate operations.

It's quite possible for one thread to read the first half of the number, then be swapped off the CPU and the second half of the value changed behind its back. Now that thread has a completely invalid value.

The docs for Interlocked.Read make explicit mention that:

The Read method and the 64-bit overloads of the Increment, Decrement, and Add methods are truly atomic only on systems where a System.IntPtr is 64 bits long. On other systems, these methods are atomic with respect to each other, but not with respect to other means of accessing the data. Thus, to be thread safe on 32-bit systems, any access to a 64-bit value must be made through the members of the Interlocked class.

(emphasis mine)

EDIT: Now that it's been edited back this looks a little silly, but I'll leave it here to make the point that if you're interlocking in some places but not others, it's kind of pointless.

like image 88
Anon. Avatar answered Oct 13 '22 10:10

Anon.