Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a method call flush the nonvolatile variable's value to the main thread?

Why does the getter Val happen to simulate volatility for the field val?

I'm assuming that leveraging a method call is not a reliable way to keep the variable volatile.

(To try it out, build for release and execute directly without the debugger.)

class Program
{
    private int val = 0;
    public int Val { get { return val; } }

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

        Task.Run(() => example.val++);

        while (example.val == 0) ; // Hangs if val is not volatile
        while (example.Val == 0) ; // Never seems to hang
    }
}
like image 236
jnm2 Avatar asked Jul 15 '14 14:07

jnm2


1 Answers

Alright, it turns out that the jitter is allowed to assume that all non-volatile variables are only accessed by one thread (much like in the C++11 memory model where concurrent accesses to non-std::atomic<> variables invoke undefined behaviour). In this case, the jitter is optimizing that first loop into loop: test eax, eax; je loop (it hoists the variable access into a register that is never updated), so obviously it never terminates.

The second loop generates assembly that reads the value relative to the object's pointer, so eventually it sees the new value (though possibly out of order with respect to other writes on the other thread, again because the variable is not volatile). This is coincidental due to the assembly that happened to be generated.

x86 assembly generated for first (infinite) loop:

003B23BA  test        eax,eax  
003B23BC  je          003B23BA  

x86 assembly for second (finite) loop:

002F2607  cmp         dword ptr [eax+4],0  
002F260B  je          002F2607

Since the jitter is allowed to assume non-volatile variables are never touched by other threads, you can only rely on volatile to work as expected (even if it appears to in a given circumstance, like this one, because future optimisations (or different CPU architectures, etc.) will probably break your code in difficult-to-debug ways).

like image 153
Cameron Avatar answered Nov 10 '22 13:11

Cameron