[cpp-threads] Failed compare-and-swap

Herb Sutter hsutter at microsoft.com
Tue Jul 31 18:44:31 BST 2007


Summarizing:

The point well illustrated by these examples is that if CAS returns the old value (rather than a bool) it can be used to perform an atomic load. If we allow that, then it should have the same default acquire semantics as a load (unless overridden in the weak/relaxed atomics of course).

Lawrence and everyone, what do you think? Now that I see this, it seems obvious in retrospect, and this question must have come up before with CAS semantics. What do other systems do?


Details follow:


Chris wrote:
> From: "Herb Sutter" <hsutter at microsoft.com>
> > Besides using Interlocked* as a way to spell a full barrier,
> > what cases do you know of where people look at the return
> > value? I haven't looked at this issue yet, and I would be
> > interested in understand such cases better.
>
> Here are two examples:
>
> http://groups.google.com/group/comp.programming.threads/msg/b6a4eec6cbb
> a625b (contrived example)

For convenience, here's the example (let's call it Example 1), which basically uses a CAS to perform an atomic read:

  // Example 1

  static CMyData my_data;
  static volatile LONG flag = 0;

  Thread 1:
    my_data.setup();
    InterlockedCompareExchange( &flag, 1, 0 ); // 0 -> 1

  Thread 2:
    while ( ! InterlockedCompareExchange( &flag, 0, 0 ) ) {
      Sleep( 0 ); _asm pause;
    }
    // flag is set
    my_data.use();

Yes, this relies on a failed CAS being a barrier, though this particular example needs it only to be an acquire barrier. The general technique here is to use a CAS purely as an atomic read.

So the question here is whether we want to support the technique of using a CAS to perform an atomic read. Do we? We can say yes by returning the old value, or no by returning a succeeded/failed bool.


> http://groups.google.com/group/comp.programming.threads/msg/57a691b2159
> fa698

OK, this is a followup where Joe Seigh points out what I said above that you could just use a plain atomic read:

  // Example 2

  ... as in Example 1, except:

  Thread 2:
    while (flag != 1)
      sleep(0);
    InterlockedCompareExchange(&flag, 1, 1);
    // flag is set
    my_data.use();

I think Joe adds the ICE call only because it seems this thread predates VS 2005 where we added more ordering semantics to volatile. In VS 2005, I you should be able to just do:

  // Example 3

  ... as in Example 1, except:

  Thread 2:
    while (flag != 1)
      sleep(0);
    // flag is set
    my_data.use();

and have that work reliably.


> http://groups.google.com/group/comp.programming.threads/msg/31803c33986
> 58e06
> (less contrived example; examine the 'initFreeQueue' function)
>
> I think the reasoning is that the InterlockedCompareExchangePointer
> does return real data.

Thanks, I didn't have time to read it carefully. So it seems to me that the point is that if you can use CAS to do a read, it should be ordered like a read (by default).

Herb





More information about the cpp-threads mailing list