Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Update "Hashcodes and Object Header Interference" for v2.11.

...

Hashcodes and Object Header Interference

*** THIS SUBSECTION NEEDS SUBSTANTIAL REWORK WITH v2.11 ***

If we have a race There are a few races that can occur between a T-deflate thread and a thread trying to get/set a hashcode (T-hash) in an ObjectMonitor:

  1. If the object has an ObjectMonitor (i.e., is inflated) and if the ObjectMonitor has a hashcode, then the

...

Start of the Race

    T-hash                  ObjectMonitor              T-deflate
    ----------------------  +-----------------------+  ----------------------------------------
    save_om_ptr() {         | owner=NULL            |  deflate_monitor_using_JT() {
      :                     | ref_count=0          | 1> cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> atomic inc ref_count  +-----------------------+
    • The data fields are at their starting values.
    • T-deflate is about to execute cmpxchg().
    • T-hash is about to increment ref_count.
    • The "1>" markers are showing where each thread is at for the ObjectMonitor box.

Racing Threads

    T-hash                  ObjectMonitor              T-deflate
    ----------------------  +-----------------------+  ------------------------------------------
    save_om_ptr() {         | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
      :   | ref_count=0   |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> atomic inc ref_count  +-----------------------+ if (contentions != 0 || waiters != 0) {
                              }
1> prev = cmpxchg(-max_jint, &ref_count, 0)
    • T-deflate has set the owner field to DEFLATER_MARKER.
    • The "1>" markers are showing where each thread is at for the ObjectMonitor box:
      • T-deflate is about to execute cmpxchg().
      • T-save is about to increment the ref_count.

T-deflate Wins

If T-deflate wins the race, then T-hash will have to retry at most once.

    T-hash                      ObjectMonitor              T-deflate
    -------------------------  +-----------------------+  ------------------------------------------
    save_om_ptr() {           | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
   1> atomic inc ref_count    | ref_count=-max_jint |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   if (owner ==           +-----------------------+  if (contentions != 0 || waiters != 0) {
          DEFLATER_MARKER &&  ||   }
        ref_count <= 0) {              \/              prev = cmpxchg(-max_jint, &ref_count, 0)
        restore obj header +-----------------------+ 1> if (prev == 0 &&
     atomic dec ref_count  | owner=DEFLATER_MARKER |     owner == DEFLATER_MARKER) {
     2> return false to   | ref_count=-max_jint |    restore obj header
     cause a retry      +-----------------------+  2> finish the deflation
} }
    • T-deflate made it past the cmpxchg() of ref_count before T-hash incremented it.
    • T-deflate set the ref_count field to -max_jint and is about to make the last of the protocol checks.
    • The first ObjectMonitor box is showing the fields at this point and the "1>" markers are showing where each thread is at for that ObjectMonitor box.
    • T-deflate sees "prev == 0 && owner == DEFLATER_MARKER" so it knows that it has won the race.
    • T-deflate restores obj header (not shown).
    • T-hash increments the ref_count.
    • T-hash observes "owner == DEFLATER_MARKER && ref_count <= 0" so it restores obj header (not shown) and decrements ref_count.
    • The second ObjectMonitor box is showing the fields at this point and the "2>" markers are showing where each thread is at for that ObjectMonitor box.
    • T-deflate finishes the deflation work.
    • T-hash returns false to cause a retry and when T-hash retries:
      • it observes the restored object header (done by T-hash or T-deflate):
        • if the object's header does not have a hash, then generate a hash and merge it with the object's header.
        • Otherwise, extract the hash from the object's header and return it.

T-hash Wins

If T-hash wins the race, then the ref_count will cause T-deflate to bail out on deflating the monitor.

Note: header is not mentioned in any of the previous sections for simplicity.

    T-hash                      ObjectMonitor              T-deflate
    -------------------------  +-----------------------+  ------------------------------------------
    save_om_ptr() {           | header=dmw_no_hash | deflate_monitor_using_JT() {
      atomic inc ref_count    | owner=DEFLATER_MARKER |   cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> if (owner ==            | ref_count=1      | if (contentions != 0 || waiters != 0) {
          DEFLATER_MARKER && +-----------------------+   }
         ref_count <= 0) {  ||  1> prev = cmpxchg(-max_jint, &ref_count, 0)
      } else {               \/              if (prev == 0 &&
   2> save om_ptr in the +-----------------------+ owner == DEFLATER_MARKER) {
       ObjectMonitorHandle | header=dmw_no_hash | } else {
        return true | owner=NULL | cmpxchg(NULL, &owner, DEFLATER_MARKER)
      } | ref_count=1 | 2> bailout on deflation
    } +-----------------------+ }
    if save_om_ptr() { ||
      if no hash \/
      gen hash & merge +-----------------------+
   hash = hash(header) | header=dmw_hash |
   } | owner=NULL |
3> atomic dec ref_count | ref_count=1 |
return hash +-----------------------+
    • T-deflate has set the owner field to DEFLATER_MARKER.
    • T-hash has incremented ref_count before T-deflate made it to cmpxchg().
    • The first ObjectMonitor box is showing the fields at this point and the "1>" markers are showing where each thread is at for that ObjectMonitor box.
    • T-deflate bails out on deflation, but first it tries to restore the owner field:
      • The return value of cmpxchg() is not checked here.
      • If T-deflate cannot restore the owner field to NULL, then another thread has managed to enter the monitor (or enter and exit the monitor) and we don't want to overwrite that information.
    • T-hash observes:
      • "owner == DEFLATER_MARKER && ref_count > 0" or
      • "owner == NULL && ref_count > 0" so it gets ready to save the ObjectMonitor*.
    • The second ObjectMonitor box is showing the fields at this point and the "2>" markers are showing where each thread is at for that ObjectMonitor box.
    • T-hash saves the ObjectMonitor* in the ObjectMonitorHandle (not shown) and returns to the caller.
    • save_om_ptr() returns true since the ObjectMonitor is safe:
      • if ObjectMonitor's 'header/dmw' field does not have a hash, then generate a hash and merge it with the 'header/dmw' field.
      • Otherwise, extract the hash from the ObjectMonitor's 'header/dmw' field.
    • The third ObjectMonitor box is showing the fields at this point and the "3>" marker is showing where T-hash is at for that ObjectMonitor box.
    • T-hash decrements the ref_count field.
    • T-hash returns the hash value.

...

  1. hashcode value can be safely fetched from the ObjectMonitor and returned to the caller (T-hash). The first stage of a racing async deflation (by T-deflate) won't affect the hashcode value that is stored in an ObjectMonitor, i.e., the race is benign.
  2. There are several reasons why we might have to inflate the ObjectMonitor in order to set the hashcode:
    1. The object is neutral, does not contain a hashcode and we (T-hash) lost the race to try an install a hashcode in the mark word.
    2. The object is stack locked and does not contain a hashcode in the mark word.
    3. The object has an ObjectMonitor and the ObjectMonitor does not have a hashcode.
      Note: In this case, the inflate() call on the common fall thru code path is almost always a no-op since the existing ObjectMonitor is not likely to be async deflated before inflate() sees that the object already has an ObjectMonitor and bails out.

The common fall thru code path (executed by T-hash) that inflates the ObjectMonitor in order to set the hashcode can race with an async deflation (T-deflate). After the hashcode has been stored in the ObjectMonitor, we (T-hash) check if the ObjectMonitor has been async deflated (by T-deflate). If it has, then we (T-hash) retry because we don't know if the hashcode was stored in the ObjectMonitor before the object's header was restored (by T-deflate). Retrying (by T-hash) will result in the hashcode being stored in either object's header or in the re-inflated ObjectMonitor's header as appropriate.

Spin-Lock Monitor List Management In Theory

...