This discussion is archived
14 Replies Latest reply: May 20, 2012 10:34 PM by 439412 RSS

Writes of references - is it always atomic?

439412 Newbie
Currently Being Moderated
Hi,

I came across the following specification which claims that "writes and reads of references are always atomic" http://docs.oracle.com/javase/specs/jls/se7/jls7.pdf.
However, i looked the assembly code generated by both open jdk's JVM and sun jdk's JVM (with PrintAssembly support) for the following code,


public class Test
{
private int a;
private Test obj;


public Test()
{
a = 5;
}

public void setValue(int b)
{
a = b;
}

public void set(Test ref)
{
obj = ref;
}

...
...
...

}

Assembly for set(Test) looked like the following

Decoding compiled method 0x00007f8c5d061110:
Code:
[Entry Point]
[Constants]
# {method} 'set' '(LTest;)V' in 'Test'
# this: rsi:rsi = 'Test'
# parm0: rdx:rdx = 'Test'
# [sp+0x20] (sp of caller)
0x00007f8c5d061240: mov 0x8(%rsi),%r10d
0x00007f8c5d061244: cmp %r10,%rax
0x00007f8c5d061247: jne 0x00007f8c5d0378a0 ; {runtime_call}
0x00007f8c5d06124d: xchg %ax,%ax
[Verified Entry Point]
0x00007f8c5d061250: push %rbp
0x00007f8c5d061251: sub $0x10,%rsp
0x00007f8c5d061255: nop ;*synchronization entry
; - Test::set@-1 (line 19)
0x00007f8c5d061256: mov %rsi,%r10
0x00007f8c5d061259: mov %rdx,%r8
0x00007f8c5d06125c: mov %r8d,0x10(%rsi)
0x00007f8c5d061260: shr $0x9,%r10
0x00007f8c5d061264: mov $0x7f8c661d7000,%r11
0x00007f8c5d06126e: mov %r12b,(%r11,%r10,1) ;*putfield obj
; - Test::set@2 (line 19)
0x00007f8c5d061272: add $0x10,%rsp
0x00007f8c5d061276: pop %rbp
0x00007f8c5d061277: test %eax,0xcc88d83(%rip) # 0x00007f8c69cea000
; {poll_return}
0x00007f8c5d06127d: retq
0x00007f8c5d06127e: hlt
0x00007f8c5d06127f: hlt


It uses two instructions mov %rdx, %r8 and mov %r8d, 0x10(%rsi). So how can this be atomic?
What if the CPU's context changed after the first instruction?
  • 1. Re: Writes of references - is it always atomic?
    796440 Guru
    Currently Being Moderated
    It may not be atomic in the hardware, but it will be atomic in the JVM.
  • 2. Re: Writes of references - is it always atomic?
    439412 Newbie
    Currently Being Moderated
    Eventually, the code is executed in the hardware, isn't it?
    If so, how can the JVM ensure atomicity?
  • 3. Re: Writes of references - is it always atomic?
    796440 Guru
    Currently Being Moderated
    sa**** wrote:
    Eventually, the code is executed in the hardware, isn't it?
    If so, how can the JVM ensure atomicity?
    The same way you or I can when we write Java:
    public class Foo {
      private String s;
      private int i.
    
      public synchronized void updateTwoVariablesAtomically(String s, int i) {
        this.s = s;
        this.i = i;
      }
    
      public synchronized int getI() { 
        return i;
      }
    
      public synchronized String getS() {
        return s;
      }
    }
    Any thread in our program will see either the original values of both s and i, or the new values of both s and i.

    Similarly, the JVM can use whatever synchronization features C++ provides to ensure that when it executes a reference write, any other thread in the JVM will see it atomically. It doesn't have to actually BE atomic down to the lowest level; it only has to be atomic at the level of the code in question--in this case, the code of the JVM. As long as the JVM does that, there's no way our Java code can see it as non-atomic. A million steps will look like one to our Java code.
  • 4. Re: Writes of references - is it always atomic?
    439412 Newbie
    Currently Being Moderated
    I can see your point with synchronization and AtomicReference. I haven't seen the assembly language code for those objects (mutex or whatever) but i think its safe to assume that assembly code (of Java using synchronization) will have additional instructions to check for lock.
    The part i don't understand is why this in-built synchronization of JVM for references doesn't translates to an assembly instruction?

    Do you have any idea about the JVM code which provides this in-built synchronization for assignment references?
    I would like to explore more on that. Thanks.
  • 5. Re: Writes of references - is it always atomic?
    796440 Guru
    Currently Being Moderated
    sa**** wrote:
    Do you have any idea about the JVM code
    None whatsoever.
  • 6. Re: Writes of references - is it always atomic?
    439412 Newbie
    Currently Being Moderated
    I modified my test program to include an explicit synchronization (just like suggested in earlier post) and as i suspected it translated to the assembly instructions,

    public void set(Test ref)
    {
    synchronized (this)
    {
    obj = ref;
    }
    }

    The assembly for this looked like this (x86 64 bit processor),

    [Entry Point]
    [Constants]
    # {method} 'set' '(LTest;)V' in 'Test'
    # this: rsi:rsi = 'Test'
    # parm0: rdx:rdx = 'Test'
    # [sp+0x40] (sp of caller)
    0x00007f713905f340: mov 0x8(%rsi),%r10d
    0x00007f713905f344: cmp %r10,%rax
    0x00007f713905f347: jne 0x00007f71390378a0 ; {runtime_call}
    0x00007f713905f34d: xchg %ax,%ax
    [Verified Entry Point]
    0x00007f713905f350: mov %eax,-0x6000(%rsp)
    0x00007f713905f357: push %rbp
    0x00007f713905f358: sub $0x30,%rsp ;*synchronization entry
    ; - Test::set@-1 (line 19)
    0x00007f713905f35c: mov %rdx,(%rsp)
    0x00007f713905f360: mov %rsi,%rbp
    0x00007f713905f363: mov (%rsi),%rax
    0x00007f713905f366: mov %rax,%r10
    0x00007f713905f369: and $0x7,%r10
    0x00007f713905f36d: cmp $0x5,%r10
    0x00007f713905f371: jne 0x00007f713905f3da
    0x00007f713905f373: mov $0xcc29d340,%r11d ; {oop('Test')}
    0x00007f713905f379: mov 0xb0(%r11),%r10
    0x00007f713905f380: mov %r10,%r11
    0x00007f713905f383: or %r15,%r11
    0x00007f713905f386: mov %r11,%r8
    0x00007f713905f389: xor %rax,%r8
    0x00007f713905f38c: test $0xffffffffffffff87,%r8
    0x00007f713905f393: jne 0x00007f713905f50e ;*monitorenter
    ; - Test::set@3 (line 19)
    0x00007f713905f399: mov (%rsp),%r10
    0x00007f713905f39d: mov %r10,%r11
    0x00007f713905f3a0: mov %r11d,0x10(%rbp)
    0x00007f713905f3a4: mov %rbp,%r10
    0x00007f713905f3a7: shr $0x9,%r10
    0x00007f713905f3ab: mov $0x7f7142670000,%r11
    0x00007f713905f3b5: mov %r12b,(%r11,%r10,1)
    0x00007f713905f3b9: mov $0x7,%r10d
    0x00007f713905f3bf: and 0x0(%rbp),%r10
    0x00007f713905f3c3: cmp $0x5,%r10
    0x00007f713905f3c7: jne 0x00007f713905f445 ;*monitorexit
    ; - Test::set@10 (line 22)


    I removed further assembly for the sake of readability, and the assembly clearly has the logic to ensure mutual exclusion of the critical section, it acquires the lock and then tries to execute the critical section.

    Having seen this, it is only fair to expect similar assembly code for JVM's in-built synchronization for reference assignment, which isn't the case (as per my first post). Can anyone clarify this?
  • 7. Re: Writes of references - is it always atomic?
    EJP Guru
    Currently Being Moderated
    It is specified in the JLS and/or the JVM Spec as being atomic. That's all you need.
  • 8. Re: Writes of references - is it always atomic?
    ++sja Explorer
    Currently Being Moderated
    sa**** wrote:
    It uses two instructions mov %rdx, %r8 and mov %r8d, 0x10(%rsi). So how can this be atomic?
    What if the CPU's context changed after the first instruction?
    That is not what the spec means by atomic. Assignment is atomic in the sense that when we have e.g. a 32-bit JVM, the four bytes that comprise a pointer are all written into memory atomically. So that if the variable originally contains

    0x11223344

    and we assign

    0xaabbccdd

    to it, any thread that is reading the variable at the same time will get either the old value or the new value, but never e.g. 0x1122ccdd. There are two mov instructions, and while the entire copy operation is not atomic, the read is, and the write is.

    The same is not true for e.g. long integers and doubles. If you do a similar assignment to a long, another thread can read a value that is half old, half new value.
  • 9. Re: Writes of references - is it always atomic?
    439412 Newbie
    Currently Being Moderated
    Thanks, that clarified. I was in a need to make sure the entire copy (from source) and write (to destination) is synchronized.
    AtomicReference solves that purpose.
  • 10. Re: Writes of references - is it always atomic?
    796440 Guru
    Currently Being Moderated
    sa**** wrote:
    Thanks, that clarified. I was in a need to make sure the entire copy (from source) and write (to destination) is synchronized.
    AtomicReference solves that purpose.
    You don't need AtomicReference in order to make reference reads and writes atomic. They always are. By definition.

    AtomicReference is useful for atomic test-and-set semantics, and for ensuring that a thread's write goes to main memory immediately instead of possibly being held in the thread's local cache. But you don't need to to make a read or a write atomic.

    Now, when you talk about the "entire copy", if you mean that when you do X = Y;, you want the read from X and the write to Y to both be contained as a single atomic unit, then AtomicReference doesn't help you with that.

    It's not really clear what you're after, though.
  • 11. Re: Writes of references - is it always atomic?
    EJP Guru
    Currently Being Moderated
    Thanks, that clarified. I was in a need to make sure the entire copy (from source) and write (to destination) is synchronized.
    AtomicReference solves that purpose.
    You are using three different concepts here as though they were the same thing.

    1. Writes of references are atomic in the JVM in the sense that writes of longs are not. The entire reference is written in one indivisible operation. A long by contrast is written in two or more operations, so it's theoretically possible for anther thread to see half the old value and half the new value, bit wise speaking.

    2. Synchronization ensures that only one thread can execute any of a set of given blocks of code at a time, where all the blocks synchronize on the same object.

    3. AtomicReference provides a means to conditionally change a reference from one known value to another if it contains the known value.

    These three things have very little to do with each other, and specifically neither (2) nor (3) is necessarily used to implement (1).
  • 12. Re: Writes of references - is it always atomic?
    439412 Newbie
    Currently Being Moderated
    Now, when you talk about the "entire copy", if you mean that when you do X = Y;, you want the read from X and the write to Y to both be contained as a single atomic unit, then AtomicReference doesn't help you with that.
    Yes, i too thought so (and was about to settle on synchronization) until i saw AtomicReference's assembly translation,


    private AtomicReference<Test> obj = new AtomicReference<Test>();

    public void set(Test ref)
    {
    obj.set(ref);
    }


    [Entry Point]
    [Constants]
    # {method} 'set' '(LTest;)V' in 'Test'
    # this: rsi:rsi = 'Test'
    # parm0: rdx:rdx = 'Test'
    # [sp+0x20] (sp of caller)
    0x00007ff0601e3240: mov 0x8(%rsi),%r10d
    0x00007ff0601e3244: cmp %r10,%rax
    0x00007ff0601e3247: jne 0x00007ff0601b98a0 ; {runtime_call}
    0x00007ff0601e324d: xchg %ax,%ax
    [Verified Entry Point]
    0x00007ff0601e3250: mov %eax,-0x6000(%rsp)
    0x00007ff0601e3257: push %rbp
    0x00007ff0601e3258: sub $0x10,%rsp ;*synchronization entry
    ; - Test::set@-1 (line 20)
    0x00007ff0601e325c: mov 0x10(%rsi),%r11d ;*getfield obj
    ; - Test::set@1 (line 20)
    0x00007ff0601e3260: test %r11d,%r11d
    0x00007ff0601e3263: je 0x00007ff0601e3292
    0x00007ff0601e3265: mov %r11,%r10
    0x00007ff0601e3268: mov %rdx,%r8
    0x00007ff0601e326b: mov %r8d,0xc(%r11)
    0x00007ff0601e326f: lock addl $0x0,(%rsp)
    0x00007ff0601e3274: shr $0x9,%r10
    0x00007ff0601e3278: mov $0x7ff0679cb000,%r11
    0x00007ff0601e3282: mov %r12b,(%r11,%r10,1) ;*synchronization entry
    ; - Test::set@-1 (line 20)
    0x00007ff0601e3286: add $0x10,%rsp
    0x00007ff0601e328a: pop %rbp
    0x00007ff0601e328b: test %eax,0xa47cd6f(%rip) # 0x00007ff06a660000
    ; {poll_return}
    0x00007ff0601e3291: retq

    There is a logic to test before entering the section to read from source and write to destination,

    0x00007ff0601e3260: test %r11d,%r11d
    0x00007ff0601e3263: je 0x00007ff0601e3292

    Edited by: sa**** on May 20, 2012 10:17 AM
  • 13. Re: Writes of references - is it always atomic?
    796440 Guru
    Currently Being Moderated
    sa**** wrote:
    Now, when you talk about the "entire copy", if you mean that when you do X = Y;, you want the read from X and the write to Y to both be contained as a single atomic unit, then AtomicReference doesn't help you with that.
    Yes, i too thought so (and was about to settle on synchronization) until i saw AtomicReference's assembly translation,
    public void set(Test ref)
    There is a logic to test before entering the section to read from source and write to destination,
    I'm not going to waste my time trying to read that assembly, and I don't know what point you're trying to make. However, what I said is correct. Specifically, if I call:
    myAtomicRef.set(X);
    then there are at least two steps that have to happen:

    1. Read the value in reference variable X.
    2. Write that value into the AtomicReference's member variable.

    Step 1 is atomic.
    Step 2 is atomic.

    The combination of steps 1 and 2 is not atomic, and the only way to make it atomic is to synchronize every access to X and ever use of that AtomicReference (or to provide the equivalent of that syncing using java.util.concurrent.Lock, etc.). Although typically there's no need for that to be atomic.

    And if hotspot has not inlined that method call (and I don't even know if that's the kind of optimization it would perform), then there are several more steps: Create a stack frame; read X; put it into its spot on the frame; put the PC into its spot on the frame; jump to the entry point of the method; read X off the stack frame; write X into the member variable; restore the PC to its previous value; drop the stack frame.

    So either you are mistaken, or you and I are talking about two completely different things. Possibly both.
  • 14. Re: Writes of references - is it always atomic?
    439412 Newbie
    Currently Being Moderated
    Ran some more tests and yes AtomicReference doesn't help in making sure entire assignment is a single atomic unit.

    Synchronization of the assignment does it. Thanks.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points