Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Use "Preformatted" paragraphs for the "Diagrams".

...

For example, when ObjectMonitor::enter() detects genuine contention via the owner field, it atomically increments the count field to indicate that the ObjectMonitor is busy. The thread calling enter() (T-enter) is potentially racing with an Async Monitor Deflation by another JavaThread (T-deflate) so both threads have to check the result of the race.

Start of the Race

...

                      ObjectMonitor              T-deflate

...

    T-enter           +-----------------------+  --------------------------------------

...

    ----------------  | owner=NULL             cmpxchg(DEFLATER_MARKER, &owner, NULL)

...

                      | count=0               |

...

                      +-----------------------+

Racing Threads

                      ObjectMonitor              T-deflate
    T-enter           +-----------------------+  --------------------------------------
    ----------------  | owner=DEFLATER_MARKER cmpxchg(DEFLATER_MARKER, &owner, NULL)
    owner contended   | count=0               :
    atomic inc count  +-----------------------+  prev = cmpxchg(-max_jint, &count, 0)

T-deflate Wins

If T-enter and T-deflate observe owner == DEFLATER_MARKER and a negative count field, then T-enter has lost the race and it retries. T-deflate will finish deflation of the ObjectMonitor.

                               ObjectMonitor              T-deflate
    T-enter                    +-----------------------+  --------------------------------------
    ----------------           | owner=DEFLATER_MARKER cmpxchg(DEFLATER_MARKER, &owner, NULL)
    owner contended            | count=-max_jint       :
    atomic inc count           +-----------------------+  prev = cmpxchg(-max_jint, &count, 0)
    if (count <= 0 && owner                               if (prev == 0 &&
        == DEFLATER_MARKER) {                                 owner == DEFLATER_MARKER) {
      restore header                                        restore header
      retry enter                                           finish the deflation
    }                                                     }

T-enter Wins

If T-enter and T-deflate observe a count field > 0, then T-enter has won the race and it can proceed with the normal contended enter work. T-deflate will detect that it has lost the race and will bailout on deflating that ObjectMonitor.

                      ObjectMonitor              T-deflate
    T-enter           +-----------------------+  --------------------------------------
    ----------------  | owner=DEFLATER_MARKER cmpxchg(DEFLATER_MARKER, &owner, NULL)
    owner contended   | count=1               :
    atomic inc count  +-----------------------+  prev = cmpxchg(-max_jint, &count, 0)
    if (count > 0)                               if (prev != 0 ||
      do contended                                   owner != DEFLATER_MARKER)
      enter work                                   bailout on deflation

An Example of Object Header Interference

...

ObjectMonitor::install_displaced_markword_in_object() is called from two places so we can have a race between a T-enter thread and a T-deflate thread:

Start of the Race

    T-enter                                    object           T-deflate
    -----------------------------------------  +-------------+  -----------------------------------------
    dmw = header()                             | mark=om_ptr dmw = header()
    if (!dmw->is_marked() &&                   +-------------+  if (!dmw->is_marked() &&
        dmw->hash() == 0) {                                         dmw->hash() == 0) {
      create marked_dmw                                           create marked_dmw
      dmw = cmpxchg(marked_dmw, &header, dmw)                     dmw = cmpxchg(marked_dmw, &header, dmw)
    }                                                           }

T-deflate Wins First Race

    T-enter                                    object            T-deflate
    -----------------------------------------  +-------------+   -----------------------------------------
    dmw = header()                             | mark=om_ptr |   dmw = header()
    if (!dmw->is_marked() &&                   +-------------+   if (!dmw->is_marked() &&
        dmw->hash() == 0) {                                          dmw->hash() == 0) {
      create marked_dmw                                            create marked_dmw
      dmw = cmpxchg(marked_dmw, &header, dmw)                      dmw = cmpxchg(marked_dmw, &header, dmw)
    }                                                            }
    // dmw == marked_dmw here                                    // dmw == original dmw here
    if (dmw->is_marked())                                        if (dmw->is_marked())
      unmark dmw                                                   unmark dmw
    obj = object()                                               obj = object()
    obj->cas_set_mark(dmw, this)                                 obj->cas_set_mark(dmw, this)

T-enter Wins First Race

    T-enter                                    object            T-deflate
    -----------------------------------------  +-------------+   -----------------------------------------
    dmw = header()                             | mark=om_ptr |   dmw = header()
    if (!dmw->is_marked() &&                   +-------------+   if (!dmw->is_marked() &&
        dmw->hash() == 0) {                                          dmw->hash() == 0) {
      create marked_dmw                                            create marked_dmw
      dmw = cmpxchg(marked_dmw, &header, dmw)                      dmw = cmpxchg(marked_dmw, &header, dmw)
    }                                                            }
    // dmw == original dmw here                                  // dmw == marked_dmw here
    if (dmw->is_marked())                                        if (dmw->is_marked())
      unmark dmw                                                   unmark dmw
    obj = object()                                               obj = object()
    obj->cas_set_mark(dmw, this)                                 obj->cas_set_mark(dmw, this)

Either Wins the Second Race

    T-enter                                    object            T-deflate
    -----------------------------------------  +-------------+   -----------------------------------------
    dmw = header()                             | mark=dmw    |   dmw = header()
    if (!dmw->is_marked() &&                   +-------------+   if (!dmw->is_marked() &&
        dmw->hash() == 0) {                                          dmw->hash() == 0) {
      create marked_dmw                                            create marked_dmw
      dmw = cmpxchg(marked_dmw, &header, dmw)                      dmw = cmpxchg(marked_dmw, &header, dmw)
    }                                                            }
    // dmw == original dmw here                                  // dmw == marked_dmw here
    if (dmw->is_marked())                                        if (dmw->is_marked())
      unmark dmw                                                   unmark dmw
    obj = object()                                               obj = object()
    obj->cas_set_mark(dmw, this)                                 obj->cas_set_mark(dmw, this)

Please notice that install_displaced_markword_in_object() does not do any retries on any code path. Instead the code adapts to being the loser in a cmpxchg() by unmarking its copy of the dmw. In the second race, if a thread loses the cas_set_mark() race, there is
also no need to retry because the object's header has been restored by the other thread.

...

If we have a race between a T-deflate thread and a thread trying to get/set a hash code (T-hash), then the first race is between the
ObjectMonitorHandle.save_om_ptr(obj, mark) call in T-hash and deflation protocol in T-deflate.

Start of the Race

    T-hash                  ObjectMonitor              T-deflate
    ----------------------  +-----------------------+  --------------------------------------
    save_om_ptr() {         | owner=NULL            cmpxchg(DEFLATER_MARKER, &owner, NULL)
      :                     | count=0               |
      atomic inc ref_count  | ref_count=0           |
                            +-----------------------+

Racing Threads

    T-hash                  ObjectMonitor              T-deflate
    ----------------------  +-----------------------+  --------------------------------------
    save_om_ptr() {         | owner=DEFLATER_MARKER cmpxchg(DEFLATER_MARKER, &owner, NULL)
      atomic inc ref_count  | count=-max_jint       if (waiters != 0 or ref_count != 0) {
                            | ref_count=1           }
                            +-----------------------+  prev = cmpxchg(-max_jint, &count, 0)

T-deflate Wins

    T-hash                    ObjectMonitor              T-deflate
    ------------------------  +-----------------------+  --------------------------------------
    save_om_ptr() {           | owner=DEFLATER_MARKER cmpxchg(DEFLATER_MARKER, &owner, NULL)
      atomic inc ref_count    | count=-max_jint       if (waiters != 0 or ref_count != 0) {
      if (owner ==            | ref_count=0           }
          DEFLATER_MARKER) +-----------------------+  prev = cmpxchg(-max_jint, &count, 0)
        atomic dec ref_count                             if (prev == 0 &&
        return false to                                      owner == DEFLATER_MARKER) {
          cause a retry                                    restore header
      }                                                    finish the deflation
                                                         }

T-hash Wins

    T-hash                    ObjectMonitor              T-deflate
    ------------------------  +-----------------------+  ----------------------------------------
    save_om_ptr() {           | owner=NULL            cmpxchg(DEFLATER_MARKER, &owner, NULL)
      atomic inc ref_count    | count=0               if (waiters != 0 or ref_count != 0) {
      if (owner ==            | ref_count=1           |    cmpxchg(NULL, &owner, DEFLATER_MARKER)
          DEFLATER_MARKER) +-----------------------+    return false to cause a retry
      }                                                  if (prev == 0 &&
      if (object no longer                                   owner == DEFLATER_MARKER) {
          has a monitor or                                 restore header
          is a different                                   finish the deflation
          monitor) {                                     }
        atomic dec ref_count
        return false to
          cause a retry
      }
      save om_ptr in the
        ObjectMonitorHandle
    }

If T-hash wins the first race, then the ref_count will cause T-deflate to bail out on deflating the monitor; ref_count is not mentioned in any of the previous examples for simplicity. If T-deflate wins the race, then T-hash will retry which will bring us to the second race.

...