Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Add "restore obj header" to "T-deflate Wins" and "T-deflate and T-hash Both Lose" subsections.

...

Once we know it is safe to deflate the monitor (which is mostly field resetting and monitor list management), we have to restore
the object's header. That's another racy operation that is described below in "Restoring the Header With Interference Detection".

...

                                   ObjectMonitor              T-deflate
    T-enter                     +-----------------------+  --------------------------------------------
    -----------------------------  | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
    owner contended             | contentions=-max_jint |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
    atomic inc contentions     +-----------------------+  :
    if (contentions <= 0 && owner                              prev = cmpxchg(-max_jint, &contentions, 0)
        == DEFLATER_MARKER) {                             if (prev == 0 &&
      restore obj header                                                        owner == DEFLATER_MARKER) {
      retry enter                                          restore objectobj header
    }                                                       finish the deflation
}
    • This diagram starts after "Racing Threads".
    • T-enter and T-deflate both observe owner == DEFLATER_MARKER and a negative contentions field.
    • T-enter has lost the race, it restores the obj header and it retries.
    • T-deflate restores the obj header and it finishes deflation of the ObjectMonitor.

...

    T-hash                     ObjectMonitor              T-deflate
    ------------------------  +-----------------------+  --------------------------------------------
    save_om_ptr() {           | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
      atomic inc ref_count    | contentions=-max_jint |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> if (owner ==           | ref_count=1           |  if (waiters != 0 || ref_count != 0) {
          DEFLATER_MARKER &&  +-----------------------+  }
        contentions <= 0) {             ||              prev = cmpxchg(-max_jint, &contentions, 0)
        atomic dec ref_count              restore obj header           \/             1> if (prev == 0 &&
     2> return false toatomic dec ref_count    +-----------------------+     owner == DEFLATER_MARKER &&
     2> return cause a retry      false to       | owner=DEFLATER_MARKER |    ref_count == 0) {
      } cause a retry             | contentions=-max_jint |    restore obj header
} | ref_count=0 | 2> finish the deflation
+-----------------------+ }
    • T-deflate made it past the first ref_count check before T-hash incremented it.
    • T-deflate set the contentions field to -max_jint and T-enter incremented the ref_count field.
    • 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-hash observes "owner == DEFLATER_MARKER && contentions <= 0" so it restores obj header (not shown) and decrements ref_count.
    • T-deflate sees "prev == 0 && owner == DEFLATER_MARKER && ref_count == 0" so it has won the race.
    • T-deflate restores obj header (not shown).
    • 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:
      • if it observes "owner == DEFLATER_MARKER && contentions <= 0" it will retry again.
      • if it observes the restored object header:
        • 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                     ObjectMonitor              T-deflate
    ------------------------  +-----------------------+  --------------------------------------------
    save_om_ptr() {           | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
      atomic inc ref_count    | contentions=-max_jint |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> if (owner ==           | ref_count=1           |  if (waiters != 0 || ref_count != 0) {
          DEFLATER_MARKER &&  +-----------------------+  }
        contentions <= 0) {             ||              prev = cmpxchg(-max_jint, &contentions, 0)
        atomicrestore obj header dec ref_count                 \/             1> if (prev == 0 &&
     2> return false toatomic dec ref_count   +-----------------------+     owner == DEFLATER_MARKER &&
     2> return false to cause a retry          | owner=NULL |    ref_count == 0) {
      } cause a retry              | contentions=0 |  } else {
} | ref_count=0 | cmpxchg(NULL, &owner, DEFLATER_MARKER)
+-----------------------+ atomic add max_jint to contentions
2> bailout on deflation
}
    • T-deflate made it past the first ref_count check before T-hash incremented it.
    • T-deflate set the contentions field to -max_jint and T-enter incremented the ref_count field.
    • 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-hash observes "owner == DEFLATER_MARKER && contentions <= 0" and starts to bail out.
    • T-deflate sees "ref_count != 0" and bails out on deflation but it has to restore some data if possible:
      • 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.
      • Add back max_jint to restore the contentions field to its proper value (which may not be the same as when we started).
    •  T-hash 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-hash returns false to cause a retry and when T-hash retries:
      • the object's header will still refer to the ObjectMonitor (i.e., not deflated)
      • it will observe "owner != DEFLATER_MARKER"
        • if the ObjectMonitor's header/dmw does not have a hash, then generate a hash and merge it with the ObjectMonitor's header/dmw.
        • Otherwise, extract the hash from the ObjectMonitor's header/dmw and return it.

Note: The addition of "restore obj header" to save_om_ptr() created a bug where the ObjectMonitor can be disconnected from the object without the ObjectMonitor being deflated. This leads to a situation where the ObjectMonitor is still on the in-use list and thinks it is owned by the object, but the object does not agree. This bug exists in the "CR1/v2.01/4-for-jdk13" version of the code that is currently out for review. The bug occurs rarely in extensive testing. I have a fix and I'm in the process of testing it.

Please note that in Carsten's original prototype, there was another race in ObjectSynchronizer::FastHashCode() when the object's monitor had to be inflated. The setting of the hashcode in the ObjectMonitor's header/dmw could race with T-deflate. That race is resolved in this version by the use of an ObjectMonitorHandle in the call to ObjectSynchronizer::inflate(). The ObjectMonitor* returned by ObjectMonitorHandle.om_ptr() has a non-zero ref_count so no additional races with T-deflate are possible.

...