8

C++0x specifies the std::atomic template for thread safe atomic access to variables. This template has, among others, a member function std::atomic::exchange that atomically stores a new value in "this" and retrieves the existing value of "this".

Win32 has a similar function: InterlockedExchange

Now, what these operations do is simple: atomic read-modify.

What I do not understand is what the point of this operation is. The value read that is returned is "meaningless", because once I can inspect the return value, another thread may already have overwritten it.

So what's the use case for this? What can the information of which value was there just before I wrote my new value into the variable tell me?

Note: The compare_exchange / InterlockedCompareExchange semantics do make sense to me, but not the simple exchange semantics.

Martin Ba
  • 37,187
  • 33
  • 183
  • 337

1 Answers1

12

Your typical spinlock:

std::atomic<bool> lock;  // initialize to false

{ // some critical section, trying to get the lock:

  while (lock.exchange(true)) { }  // now we have the lock

  /* do stuff */

  lock = false; // release lock
}

See Herb Sutter's wait-free queue for a real-world application.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • nvm that last bit (wasn't thinking :P), but just be careful with Herb's older Dr Dobbs articles, some where found to have serious flaws (found by Herb himself) – Necrolis Aug 10 '11 at 08:31
  • @Necrolis: Indeed, that article itself is a follow-up to a correction of a previous article by someone else that got it wrong. (Then again, some people have even tried to argue that you don't need atomics at all and can just fake a mutex with a standard bool, maybe made volatile :-) .) – Kerrek SB Aug 10 '11 at 08:46
  • according to intels x86 manuals, you can/could (see Vol 3A Sec 8.1), but using proper atomics is the only way to go :) – Necrolis Aug 10 '11 at 08:58
  • @Necrolis: Well, how would you write the `exchange` in standard-C++? I don't think there's a way to do that correctly in the presence of multiple threads. – Kerrek SB Aug 10 '11 at 10:41
  • you wouldn't need `exchange`, according to intel, byte reads and writes are atomic on x86, so `volatile bool lock = false; while(!lock); lock = true;`(wrapping the lock in a scoped class would make it safer to use). however, I have a feeling this would break down on multi-core cpu's where the bus requires locking for correct atomicity – Necrolis Aug 10 '11 at 11:21
  • 3
    @Necrolis: I'm not convinced. Suppose `lock` is false. You say `while(!lock);` and move past that point. Simultaneously, another thread does the same. Only now do you get to setting `lock` to true. Now two threads have moved into the same critical section at once. – Kerrek SB Aug 10 '11 at 14:25
  • 1
    @Necrolis: what if your thread is pre-empted after exiting the loop, but before the assignment? Another waiting thread can now also exit the loop and have "acquired" the lock. – R. Martinho Fernandes Aug 10 '11 at 14:28
  • Good points, just felt like playing the other side for once. glad I stick to atomic CAS for 'simple' mutexes :) – Necrolis Aug 10 '11 at 14:31
  • That code will sure burn the processor core, just like spinlock with infinite spin count. – Gene Bushuyev Aug 10 '11 at 17:03
  • @Necrolis -- you do need atomic exchange; only single x86 instructions are atomic, not read followed by write. But x86 has an xchg instruction to swap register and memory, which is atomic. – Gene Bushuyev Aug 10 '11 at 17:08
  • @Gene: It's to be used judiciously, of course. The prime example is the waitfree queue I'm linking, in which you're at best waiting for two or three pointer assignments. Spinning is sometimes preferable over a mutex, because you don't force a context switch. – Kerrek SB Aug 10 '11 at 17:09
  • It's worth mentioning that this lock is not re-entrant, so if your function is recursive it will deadlock. – Gene Bushuyev Aug 10 '11 at 17:41
  • Actually, applying some more thought: `unsigned char lock = 0; while(_bittestandset(&lock,0)){_mm_pause();}` that should nievely work, however, I know `BTS` is more than one uop, so its not actually going to be atomic without a LOCK prefix (even though its one instruction doing a read & write). – Necrolis Aug 10 '11 at 18:52
  • @Necrolis - all single instructions on x86 are atomic, there is an implicit lock where necessary. – Gene Bushuyev Aug 10 '11 at 21:34
  • Isn't this example incomplete without memory fences? I know that x86 doesn't reorder, still in general case, fences are necessary. – Gene Bushuyev Aug 10 '11 at 21:44