Does MemoryBarrier guarantee memory visibility for all memory?

If I understand correctly, in C #, the lock block guarantees exclusive access to the instruction set, but also ensures that any reads from memory reflect the latest version of this memory in any processor cache. We consider lock blocks to protect variables read and modified inside the block, which means:

  • Assuming that you have properly implemented locking where necessary, these variables can only be read and written in one thread, and
  • Read in the lock block, see in the latest versions of the variable and write in the lock block becomes visible for all threads.

(right?)

This second point interests me. Is there any magic due to which only variables read and written in the code protected by the lock block are guaranteed fresh, or are the memory barriers used in the implementation of lock guarantee that all memory is now equally fresh for all threads? Forgive my mental awkwardness here about how caches work, but I read that several multibyte "lines" of data are stored in caches. I think I am asking if the memory barrier makes the forced synchronization of all dirty cache lines or only some, and if only some, which determines which lines are synchronized?

+2
source share
2 answers

Read in the block block, see the latest versions of the variable and write in the block block is visible for all threads.

No, this is definitely a harmful simplification.

When you enter the lock statement, there is a memory fence, which means that you will always read "fresh" data. When you exit the lock state, there is a memory fence, which means that all the data you record is guaranteed to be written to the main memory and available for other streams.

The important point is that if several threads only ever read / write memory when they "own" a particular lock, then by definition one of them will exit the lock before the next one starts it ... so all these readings and the entries will be simple and correct.

If you have code that reads and writes to a variable without committing, then there is no guarantee that it will "see" data written with good code (for example, code using locks), or that well-executed threads will be "see" data written by this bad code.

For instance:

 private readonly object padlock = new object(); private int x; public void A() { lock (padlock) { // Will see changes made in A and B; may not see changes made in C x++; } } public void B() { lock (padlock) { // Will see changes made in A and B; may not see changes made in C x--; } } public void C() { // Might not see changes made in A, B, or C. Changes made here // might not be visible in other threads calling A, B or C. x = x + 10; } 

Now this is more subtle than that, but why using shared locking to protect a set of variables works.

+6
source

If I understand correctly, in C # a blocking block guarantees exclusive access to a set of instructions ...

Right The specification guarantees this.

but it also ensures that any reads from memory reflect the latest version of that memory in any processor cache.

The C # specification says nothing about the "CPU cache". You left the area of ​​what is guaranteed by the specification, and entered the area of ​​implementation details. There is no requirement that a C # implementation be executed on a processor with any particular cache architecture.

Is there any magic that only variables read and written in the code protected by the blocking block are guaranteed to be fresh, or do the memory barriers used in the implementation of the blocking ensure that all memory is now equally fresh for all threads?

Instead of trying to make out your question or question, say what is actually guaranteed by the language. Special effect:

  • Any write to a variable, unstable or not
  • Any reading of a volatile field
  • Any throws

The order of special effects is stored at specific special points:

  • Read and write variable fields
  • locks
  • thread creation and termination

Runtime is required to ensure that special effects are ordered sequentially with special points. So, if the volatile field is read before the lock, and then the record is written after that, the reading cannot be moved after the record.

So how is this achieved at runtime? Beats me up. But runtime, of course, is not required to "ensure that all memory is fresh for all threads." Runtime is required to ensure that certain readings, records, and outliers are performed in chronological order with respect to specific points and that is all.

Runtime, in particular, does not require all threads to follow the same order.

Finally, I always end such discussions, pointing to you here:

http://blog.coverity.com/2014/03/26/reordering-optimizations/

After reading this, you should get a mark for the terrible things that can happen even on x86 when you play at ease about castles.

+7
source

Source: https://habr.com/ru/post/1262811/


All Articles