1

I think I slightly got the idea what exactly Volatile.Write and Volatile.Read do, but I've seen some examples where Volatile.Write is used at the beginning of a method like in the CLR via C# book where Jeffrey shows how to implement a Simple Spin Lock using Interlocked. Here's the code:

struct SimpleSpinLock {
   private int _resourceInUse; // 0 = false, 1 = true

   public void Enter() {
      while (true) {
         if (Interlocked.Exchange(ref _resourceInUse, 1) == 0) return;
      }
   }

   public void Leave() {
      Volatile.Write(ref _resourceInUse, 0); // Why is it here??
   }
}

This is how the class is suppose to be used:

class SomeResource {
   private SimpleSpinLock _s1 = new SimpleSpinLock();

   public void AccessResource() {
      _s1.Enter();
      // Some code that only one thread at a time can get in..
      _s1.Leave();
   }
}

So as I know Volatile.Write is used to guarantee that instructions which are above it will be executed exactly before the Volatile.Write. But in the Leave method there is only one instruction and what's the reason to use Volatile.Write here? Probably I understand the things completely wrong, so I'd be grateful if someone could lead me to the right way.

E. Shcherbo
  • 1,128
  • 8
  • 15
  • 1
    I don't like combining [`Interlocked`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked) and [`Volatile`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.volatile). The former is an easy-to-understand, no-surprises mechanism, while the later is accompanied with all kind of weird semantics that require an 150-IQ brain to understand. In this case using `Volatile` is not even required. The following version of `Leave` is equally efficient and functionally equivalent: `public void Leave() => Interlocked.Exchange(ref _resourceInUse, 0);` – Theodor Zoulias May 08 '20 at 16:05
  • 1
    @TheodorZoulias you made it a bit clearer. Thank you and yeah, I agree with that the `Volatile` is a mess (at least for me). Some samples are understandable, but other ones just like to make you feel stupid or to make you spend ages to figure out why it there :) – E. Shcherbo May 08 '20 at 18:43

2 Answers2

3

Would not claim that I have enough brainpower to fully understand/explain this, but here are my 5 cents. First of all compiler can inline Leave method call cause it consists of only one line, so actual write can be surrounded by other instructions. Secondary (and mainly, I suppose) the Volatile class docs state next:

On a multiprocessor system, a volatile write operation ensures that a value written to a memory location is immediately visible to all processors.

So the goal of this Volatile.Write call to make the change seen to other processor as soon as possible.

Also see answers to this question and read about volatile keyword

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • This explanation makes sense for this specific case, I guess, because it's important for a thread in the while loop to see changes exactly after they have been made. – E. Shcherbo May 07 '20 at 19:37
  • Another thing that concerns me is I think another process will see changes only if it uses `Volatile.Read`, but in this particular example it doesn't use. – E. Shcherbo May 07 '20 at 19:46
  • 2
    It uses `Interlocked.Exchange` which should have same effect, I suppose. – Guru Stron May 07 '20 at 19:56
  • Then I don't see any sense to use `Volatile.Write`. As I know, write to integer is an atomic operation, so even if we write there without `Volatile.Write` and just like _resourceInUse = 0, then `Interlocked.Exchange` still will be able to see changes. Does it make sense? – E. Shcherbo May 07 '20 at 20:05
  • 1
    @E.Shcherbo still inlining argument stays. – Guru Stron May 07 '20 at 20:23
  • I see your point, thank you. Let me wait a while and see thoughts of other people before accepting it. – E. Shcherbo May 07 '20 at 21:04
  • 2
    @E. Shcherbo without `Volatile.Write` the new value of _resourceInUse = 0 can still remain in CPU cache and won't be visible to the code on another CPU core that is running `Interlocked.Exchange`. As I understand `Interlocked.Exchange` ensures working with values directly in memory, I do not think it forces other cores to flush their caches – Igor B May 07 '20 at 22:09
  • @Igor B I see what you mean. Yeah makes sense to me. Thanks! – E. Shcherbo May 08 '20 at 03:51
0

Volatile.Write

"Writes a value to a field. On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: If a read or write appears before this method in the code, the processor cannot move it after this method."

The way I understand this is:

The method this sentence is referring to is Volatile.Write(). So, "if a read or write operation appears before Volatile.Write in the code, the processor cannot move it after Volatile.Write". That means if another thread (for example) is reading/writing on the same resource it must wait for the Volatile.Write to execute before being scheduled by the processor.

It makes sense, no? I don't think it's a matter of position of read/write instructions in the "hosting" method (Leave), but it's more about read/writes "happening" in between Enter and Leave.

FandangoOnCore
  • 269
  • 2
  • 9
  • Probably yes, but I'm not sure if there is any sense to do so in this spin lock implementation. I suppose that this _On a multiprocessor system, a volatile write operation ensures that a value written to a memory location is immediately visible to all processors_ makes more sense, but I'm not sure. I've read some articles and SO threads on it, but it's still unclear. – E. Shcherbo May 07 '20 at 19:35
  • But do you think it is possible for compiler (or whatever optimizes the code) to reorder reading using `Interlocked.Exchange(ref _resourceInUse, 1) == 0` with `Volatile.Write(ref _resourceInUse, 0);` if there weren't `Volatile`?? I don't think this is possible. – E. Shcherbo May 07 '20 at 19:41
  • As far as I understand, the article is talking about processor optimizations, not compile time optimizations. And yes, I must admit I don't have a solid knowledge on the subject, but as far as I know the processors (especially newer ones) can alter the sequence of the instructions for optimization, for example by executing instructions on the availability of operands. [link](https://www.cs.uaf.edu/2011/spring/cs641/proj1/vsanditi/) "In other words, processor that uses multiple execution units completes the processing of instructions in wrong order." – FandangoOnCore May 07 '20 at 19:59