1

In order to perform interlocked reads I have used InterlockedCompareExchange function a la

value = InterlockedCompareExchange(ptr, 0, 0);

Now I stumbled upon unusual situation.
I want to use this interlocked "read" on a read-only (write-protected) memory page. The result is "Access violation writing location x"...

So I guess this is by design? I understand that InterlockedCompareExchange is often a used as a "write" instruction. But why is access violation trigger included in this particular case?

What are the alternatives to InterlockedCompareExchange in order to perform interlocked read?


Update: I tried using

value = InterlockedCompareExchange(ptr, 0, -1);

when I know that the value at ptr is never negative, but this triggers the access violation also, even though in this case there certainly isn't any writing going on. So I guess this is by design with this instruction...
But why and how to get around that?


Update2: More details about why I started using interlocked reads.
I left my question intentionally vague. I am generally interested in the topic, that is - both skipping the cache and having atomic reads, including atomic 64-bit reads.

The reason why I started wondering is that .NET uses CompareExchange for interlocked reading inside
System.Threading.Interlocked.Read(ref Int64 location)
method.
The MSDN states for this .NET Interlocked.Read method: "The Read method is unnecessary on 64-bit systems, because 64-bit read operations are already atomic. On 32-bit systems, 64-bit read operations are not atomic unless performed using Read."

Also, MSDN states for (C-language) InterlockedCompareExchange64 function that unaligned operations are not permitted in any case: "The variables for this function must be aligned on a 64-bit boundary; otherwise, this function will behave unpredictably on multiprocessor x86 systems and any non-x86 systems".
So according to that text also .NET should never have unaligned Int64 variables. But then why interlocked reading?

Roland Pihlakas
  • 4,246
  • 2
  • 43
  • 64

1 Answers1

4

The memory must be writable in case the comparison succeeds. This is true even if you know that it won't succeed due to your program logic. On x86, this is enforced by the processor. According to the processor documentation, the cmpxchg instruction generates a write cycle even if the comparison fails.

It is not clear what the intention of the interlocked operation is. If you merely want to read the value atomically (i.e., without tearing), then you can simply issue a read, because properly aligned reads and writes are already atomic. If you want to access the memory with specific semantics, you can stick a _ReadBarrier, _WriteBarrier, MemoryBarrier, or fence before/after the instruction as desired. Starting in Visual Studio 2005, volatile reads are performed with acquire semantics and volatile writes are performed with release semantics.

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • Thanks for Your answer! I left explanation about the reasoning behind the question in the Update2 of the question. This comment box was too small to contain it. I would like to confirm: does the "properly aligned reads and writes are already atomic" apply under 32-bit code that is reading 64-bit variables also? I apologize that I did not mention 64-bit from the start. I was trying to be intentionally vague, but I should have been more explicit. – Roland Pihlakas Jan 01 '14 at 17:58
  • @RolandPihlakas [MSDN explains](http://msdn.microsoft.com/library/ms684122): "Reads and writes to 64-bit values are not guaranteed to be atomic on 32-bit Windows." You can check your CPU documentation to see if for example `movq` is atomic on 32-bit systems but that is not something covered by Win32. See also [this SO answer](http://stackoverflow.com/a/6569156/902497). – Raymond Chen Jan 01 '14 at 18:11
  • Thanks, I guess that settles the question. I started using _mm_loadl_epi64 intrinsic which should correspond to the movq instruction, which is atomic in Intel processors when reading from aligned addresses: http://www.intel.com/Assets/PDF/manual/253668.pdf I assume this applies to all x86 processors. The only issue for me with movq is that it requires SSE2 support from the CPU. – Roland Pihlakas Jan 03 '14 at 10:22
  • Citation from the above document: The Pentium processor (and newer processors since) guarantees that the following additional memory operations will always be carried out atomically: * Reading or writing a quadword aligned on a 64-bit boundary ... The P6 family processors (and newer processors since) guarantee that the following additional memory operation will always be carried out atomically: * Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line. – Roland Pihlakas Jan 03 '14 at 10:23
  • Wanted to point out that using Visual Studio's `volatile` and relying on its release/acquire leads to awful portability problems. When built on other compilers the code will *appear* to work but mysteriously fail due to subtle race conditions. – Zan Lynx Apr 25 '16 at 16:49