Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Update "An Example of ObjectMonitor Interference Detection" subsection for v2.11.

...

An Example of ObjectMonitor Interference Detection

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

ObjectMonitor::save_om_ptrenter() is used to safely save an ObjectMonitor* in an ObjectMonitorHandlecan change an idle monitor into a busy monitor. ObjectSynchronizer::deflate_monitor_using_JT() is used to asynchronously deflate an idle monitor. save_om_ptrenter() and deflate_monitor_using_JT() can interfere with each other. The thread calling save_om_ptrenter() (T-saveenter) is potentially racing with another JavaThread (T-deflate) so both threads have to check the results of the races.

Start of the Race

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

Racing Threads

    T-save           enter                   ObjectMonitor              T-deflate
    ------------------------ +-----------------------+  --------------------------------------------
    save_om_ptrenter() { | owner=DEFLATER_MARKER | deflate_monitor_using_JT() {
   1> atomic inc ref_countcontentions | ref_countcontentions=0            |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
      +-----------------------+  :
1> prev = cmpxchg(-max_jint, &ref_countcontentions, 0)
    • T-deflate has executed cmpxchg() and set owner to DEFLATER_MARKER.
    • T-save enter still hasn't done anything yet
    • The "1>" markers are showing where each thread is at for the ObjectMonitor box:
      • T-save enter and T-deflate are racing to update the ref_count contentions field.

T-deflate Wins

    T-save                enter                            ObjectMonitor             ObjectMonitor                T-deflate
    --------------------------------- +-------------------------+  --------------------------------------------
    save_om_ptrenter() {   | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
    atomic inc ref_countcontentions    | ref_countcontentions=-max_jint+1 |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> if (owner == DEFLATER_MARKER && +-------------------------+  :
    ref_countcontentions <= 0) {                         ||              prev = cmpxchg(-max_jint, &ref_countcontentions, 0)
        restore obj header                   \/                  1> if (prev == 0 &&
      atomic dec ref_count           contentions         +-------------------------+        owner == DEFLATER_MARKER) {
     2> return false to force retry    | owner=DEFLATER_MARKER | restore obj header
    }                                | ref_countcontentions=-max_jint |   2> finish the deflation
+-------------------------+ }
    • This diagram starts after "Racing Threads".
    • The "1>" markers are showing where each thread is at for that ObjectMonitor box:
      • T-save enter and T-deflate both observe owner == DEFLATER_MARKER and a negative ref_count contentions field.
    • T-save enter has lost the race: it restores the obj header (not shown) and decrements the ref_countcontentions.
    • T-deflate restores the obj header (not shown).
    • The "2>" markers are showing where each thread is at for that ObjectMonitor box.
    • T-save enter returns false to cause the caller to retry.
    • T-deflate finishes the deflation.

T-

...

enter Wins

    T-save          enter                            ObjectMonitor                T-deflate
    --------------------------------- +---------------------------------------------------------------------
    save_om_ptrenter() { | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
    atomic inc ref_count  contentions   | ref_countcontentions=1          |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> if (owner == DEFLATER_MARKER && +-------------------------+  :
    ref_countcontentions <= 0) {                   ||                                prev = cmpxchg(-max_jint, &ref_countcontentions, 0)
    } else {     }            \/         1> if (prev == 0 &&
      2> <continue savecontended om_ptrenter> in the           +-------------------------+         owner == DEFLATER_MARKER) {
ObjectMonitorHandle | owner=NULL | } else {
| } else {
2> return true | ref_countcontentions=1          | cmpxchg(NULL, &owner, DEFLATER_MARKER)
+-------------------------+ 2> return
    • This diagram starts after "Racing Threads".
    • The "1>" markers are showing where each thread is at for the ObjectMonitor box:
      • T-save enter and T-deflate both observe a ref_count contentions field > 0.
    • T-save enter has won the race and it saves the ObjectMonitor* in the ObjectMonitorHandle (not shown)continues with the contended enter protocol.
    • T-deflate detects that it has lost the race (prev != 0) and bails out on deflating the ObjectMonitor:
      • Before bailing out T-deflate tries to restore the owner field to NULL if it is still DEFLATER_MARKER.
    • The "2>" markers are showing where each thread is at for that ObjectMonitor box.

...

    T-enter                                       ObjectMonitor                T-deflate
    -------------------------------------------- +-------------------------+  --------------------------------------------
    ObjectMonitor::enter() { | owner=DEFLATER_MARKER |  deflate_monitor_using_JT() {
    <ownerincrement iscontentions   contended>   | ref_countcontentions=1                   |  cmpxchg(DEFLATER_MARKER, &owner, NULL)
   1> EnterI() {   +-------------------------+ 1> :
  if (owner == DEFLATER_MARKER && || 2> : <thread_stalls>
      cmpxchg(Self, &owner,                    \/ :
    DEFLATER_MARKER) +-------------------------+ :
== DEFLATER_MARKER) { | owner=Self/T-enter | :
// EnterI is done | ref_countcontentions=0 | : <thread_resumes>
return +-------------------------+ prev = cmpxchg(-max_jint, &ref_countcontentions, 0)
} || if (prev == 0 &&
} // enter() is donedecrement contentions \/ 3> owner == DEFLATER_MARKER) {
} // ~OMH: atomic dec ref_countenter() is done +-------------------------+ } else {
2> : <does app work> | owner=Self/T-enter|NULL | cmpxchg(NULL, &owner, DEFLATER_MARKER)
3> : | ref_countcontentions=-max_jint | atomic add max_jint to ref_countcontentions
exit() monitor +-------------------------+ 4> bailout on deflation
4> owner = NULL || }
\/
+-------------------------+
| owner=Self/T-enter|NULL |
| ref_countcontentions=0 |
+-------------------------+
    • T-deflate has executed cmpxchg() and set owner to DEFLATER_MARKER.
    • T-enter has called ObjectMonitor::enter() with "ref_count == 1", noticed that the owner is contended, increments contentions, and is about to call ObjectMonitor::EnterI().
    • 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 stalls after setting the owner field to DEFLATER_MARKER.
    • T-enter calls EnterI() to do the contended enter work:
      • EnterI() observes owner == DEFLATER_MARKER and uses cmpxchg() to set the owner field to Self/T-enter.
      • T-enter owns the monitor , and returns from EnterI(), .
    • T-enter decrements contentions and returns from enter().The ObjectMonitorHandle destructor decrements the ref_count.
    • T-enter is now ready to do work that requires the monitor to be owned.
    • 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-enter is doing app work (but it also could have finished and exited the monitor).
    • T-deflate resumes, calls cmpxchg() to set the ref_count contentions field to -max_jint, and passes the first part of the bailout expression because "prev == 0".
    • The third ObjectMonitor box is showing the fields at this point and the "3>" markers are showing where each thread is at for that ObjectMonitor box.
    • T-deflate performs the A-B-A check which observes that "owner != DEFLATER_MARKER" and bails out on deflation:
      • Depending on when T-deflate resumes after the stall, it will see "owner == T-enter" or "owner == NULL".
      • Both of those values will cause deflation to bailout so we have to conditionally undo work:
        • restore the owner field to NULL if it is still DEFLATER_MARKER (it's not DEFLATER_MARKER)
        • undo setting ref_count contentions to -max_jint by atomically adding max_jint to ref_count contentions which will restore ref_count contentions to its proper value.
      • If the T-enter thread has managed to enter but not exit the monitor during the T-deflate stall, then our owner field A-B-A transition is:
        • NULL → DEFLATER_MARKER → Self/T-enter

      • so we really have A1-B-A2, but the A-B-A principal still holds.

      • If the T-enter thread has managed to enter and exit the monitor during the T-deflate stall, then our owner field A-B-A transition is:

        • NULL → DEFLATER_MARKER → Self/T-enter  → NULL

      • so we really have A-B1-B2-A, but the A-B-A principal still holds.

    • T-enter finished doing app work and is about to exit the monitor (or it has already exited the monitor).

    • The fourth ObjectMonitor box is showing the fields at this point and the "4>" markers are showing where each thread is at for that ObjectMonitor box.

...