0

I cannot use C++11 atomics here

I have a doubt about my use of InterlockedCompareExchange() in order to 'atomically' read a variable.

I asked a question about this here, but what I was doing there was different. The "exchange" and "comperand" parameters (2nd and 3rd) were 'hard coded' values, i.e. not read from a variable.

Please consider this:

    // Copy the connect time
    DWORD dwConnectTime = InterlockedCompareExchange(&msgInfo.m_dwConnectTime, 
                            msgInfo.m_dwConnectTime, 
                            msgInfo.m_dwConnectTime);

This is intended to swap the value of msgInfo.m_dwConnectTime with the current value of msgInfo.m_dwConnectTime, provided the current value of msgInfo.m_dwConnectTime is msgInfo.m_dwConnectTime. The previous value of msgInfo.m_dwConnectTime is then returned, which is what I rely on to 'copy' the value.

It has just dawned on me though that the reads of msgInfo.m_dwConnectTime for the second and third parameters themselves are not guaranteed to be atomic. Thus, is this code incorrect hence I need to use a locking primitive to copy msgInfo.m_dwConnectTime?

Wad
  • 1,454
  • 1
  • 16
  • 33
  • Why are you swapping the current value of a variable with itself? This should be a no-op. – dbush May 01 '18 at 14:28
  • This makes no sense. It's almost as if you believe that this one variable can hold two values simultaneously. – David Heffernan May 01 '18 at 14:30
  • As the linked question shows, I can use this to "atomically read" the value of a variable without risk of getting a torn read or write. That's what I'm trying to do here. – Wad May 01 '18 at 14:33
  • 2
    Reading is atomic if the variable is aligned. And the variable must be aligned to use `InterlockedCompareExchange`. So the accepted answer at the linked question is totally bogus. You seem to be getting ever more confused about issues that really are not that complex. – David Heffernan May 01 '18 at 14:37
  • What about if I read and write to the same memory location on two different threads each running on a separate core? Without the `LOCK` instruction surely tearing could happen there... – Wad May 01 '18 at 14:42
  • 1
    If you just want to read a value using Interlocked functions, you can use `InterlockedExchangeAdd()` instead, eg: `DWORD dwConnectTime = InterlockedExchangeAdd(&msgInfo.m_dwConnectTime, 0);` That will return the "previous" value of `m_dwConnectTime` after incrementing it by 0, IOW return its "current" value – Remy Lebeau May 01 '18 at 14:43
  • Thanks @Remy Lebeau. Maybe you could shed some light on my above question? – Wad May 01 '18 at 14:46
  • you not need any interlocked/lock here. read *dword* value (if it *dword* aligned) already atomic operation. and this not add any synchronization with another threads. – RbMm May 01 '18 at 14:50
  • Yes *reading* might be but what if I read whilst something else is *writing* as can happen on a machine with more than one core??? – Wad May 01 '18 at 14:51
  • @Wad - and so what ? are you understand situation ? atomic read and synchronization with another threads - absolute different things. in anyway you read some value. this value can changed at any time by another thread. but try understand different between atomic read and tread synchronization – RbMm May 01 '18 at 14:54
  • Tearing is impossible for aligned data – David Heffernan May 01 '18 at 14:55
  • Yes I do understand the difference between an atomic read and thread synchronisation. I am not trying to protect a critical section of code, I am merely trying to grab a "snapshot" of a variable without having to worry if another CPU is currently writing to it when I try and read it. – Wad May 01 '18 at 14:57
  • 1
    @RemyLebeau Pointless to do that. Just read the value directly. Aligned access is atomic. – David Heffernan May 01 '18 at 14:59
  • 2
    [*The Intel486 processor (and newer processors since) guarantees that the following basic memory operations will always be carried out atomically .. Reading or writing a doubleword aligned on a 32-bit boundary*](https://www.coursehero.com/file/p5gtl9k/711-Guaranteed-Atomic-Operations-The-Intel486-processor-and-newer-processors/) – RbMm May 01 '18 at 15:04
  • 1
    in case you want *read and change*, say `x = x + 1` here need interlock operation. for read value only(register size, aligned) - not need lock. value after you read it can changed at any time, but this already another question – RbMm May 01 '18 at 15:08

1 Answers1

3

Based on the comments, and the link to your earlier question, this question is motivated by your desire to avoid tearing. Reading from and writing to aligned data is atomic. You are attempting to guard against tearing, but tearing is not possible when the data is aligned. And it is reasonable to assume that your data is aligned because that is a requirement of InterlockedCompareExchange and indeed all the InterlockedXXX functions.

As such, the question you are asking is something of a non sequitur. It is based on the false premise that tearing can happen with aligned data.

So, you don't need to call InterlockedCompareExchange or any other InterlockedXXX function in order to avoid tearing, because tearing is only possible when the data is not aligned.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • OK, so what happens if thread A reads and thread B writes (both running on separate cores) at **absolutely, precisely the same time**. Surely there's potential for a tear there? Otherwise how is it determined which one goes first? Isn't that what `LOCK` is for, to prevent anyone else accessing that memory?? If this can't happen, then why do we need `LOCK`? – Wad May 01 '18 at 15:00
  • @Wad thread A read old or new value. but atomic. note that we try not **change** but only read. – RbMm May 01 '18 at 15:01
  • 2
    Perhaps you don't know what tearing is? Tearing is where you have data that straddles a cache line. In order to read, or write it, two memory operations are performed, one on either side of the cache line boundary. If the two read operations interleave with two write operations, then you get partial reads. That's tearing. However, with aligned data, the memory is all within a single cache line and reading and writing are serialized by the memory bus. They can happen in an unpredictable order, but they never tear. – David Heffernan May 01 '18 at 15:04
  • Right. OK. I've read that a few times and clearly I didn't know what tearing was. Thanks for clarifying. "*reading and writing are serialized by the memory bus*" - is that an answer aimed specifically at me asking what happens if a read and write occur at precisely the same? In other words, they can't happen at the same time? – Wad May 01 '18 at 15:18
  • 1
    Exactly. Whilst the two processors may reach read and write instructions to the same cache line at the same time, when performing those instructions the memory bus makes sure that they happen one at a time. – David Heffernan May 01 '18 at 15:23
  • @DavidHeffernan I've posted a follow up at https://stackoverflow.com/q/50121673/2369597, would be greatly appreciative if you could participate. – Wad May 01 '18 at 18:20
  • This was all covered by Anders answer in the previous question. You should have accepted that answer. – David Heffernan May 01 '18 at 18:55