Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

volatile, Interlocked: trying to break code

First of all, I was trying to find out if using Interlocked still requires volatile field definition, and that is my real question.

But. Being too lazy to analyse generated MSIL I decided to check it in practice.

I'm trying MSDN example for volatile usage when code should break in release build with optimizations on. And nothing breaks. Code works fine (in this case - main thread terminates gracefully) with optimizations on and off.

  1. Do I still require volatile keyword on a field when I'm writing to it from one thread using Interlocked and reading from another thread without lock?
  2. Simple example of code from 1-st question where volatile makes difference?
  3. Why does MSDN example still working when I remove volatile keyword and build in release?

Code snippet to illustrate question 1.

class Example
{
    volatile int val;

    void Do()
    {
        Task.Run(() => { while (val == 0) Console.WriteLine("running"); });

        Thread.Sleep(1000);
        Interlocked.Increment(ref val);
        Console.WriteLine("done.");
        Console.ReadLine();
    }
}
like image 995
Anri Avatar asked Feb 04 '13 16:02

Anri


2 Answers

If the variable is not marked as volatile, then code that reads it cannot know that it might be modified by some other thread. So the JIT compiler won't know that keeping the value in a register is potentially unsafe.

volatile is how you tell the JIT compiler that other threads can modify the value. That the other thread might use Interlocked when changing the value is irrelevant because the JITer might not even know that the other code exists!

It might be that your simple example works because the JIT compiler can see all of the code. Things might be much different if those two bits of code were in separate methods or even different assemblies altogether.

like image 163
Jim Mischel Avatar answered Sep 21 '22 10:09

Jim Mischel


Well, i managed to produce code where volatile does make a difference and Interlocked does not help. You should run release build without debugger to test it.

public class Example
{
    private volatile int val = 0;

    public static void Main()
    {
        var example = new Example();

        Task.Run(() => Interlocked.Increment(ref example.val));

        while (example.val == 0) ;
        // this never happens if val is not volatile
        Console.WriteLine("done.");
        Console.ReadLine();
    }
}

I'm going to accept @JimMischel's answer as it has some explanations, if nothing more detailed appears.

UPDATE: also found this blog post explaining some details.

like image 33
Anri Avatar answered Sep 21 '22 10:09

Anri