Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Add notes to make it clear which code and analysis is for v2.06 (current version being reviewed) and v2.07 (the next version). Move helper function subsections to match what v2.07 needs.

...

Note: The above code snippet comes from ObjectSynchronizer::prepend_block_to_lists(); see that function for more complete context (and comments).

Note: In v2.06, L2 uses OrderAccess::load_acquire() and L3 uses OrderAccess::release_store(). David H. pointed out that a regular load and regular store can be used. I've made the change for the upcoming V2.07 and will remove this note when the project rolls forward to v2.07.

The Not So Simple Case or  Taking and Prepending on the Same List Leads to A-B-A Races

...

The next case to consider for lock-free list management with the Java Monitor subsystem is prepending to a list that also allows deletes. As you might imagine, the possibility of a prepend racing with a delete makes things more complicated. The solution is to "mark" the next field in the ObjectMonitor at the head of the list we're trying to prepend to. A successful mark tells other prependers or deleters that the marked ObjectMonitor is busy and they will need to retry their own mark operation.

Note: This is the v2.06 version of code and associated notes:

    L01:  while (true) {
L02: ObjectMonitor* cur = OrderAccess::load_acquire(list_p);
L03:    ObjectMonitor* next = NULL;
L04:    if (!mark_next(m, &next)) {
L05:      continue;  // failed to mark next field so try it all again
L06:    }
L07:    set_next(m, cur);  // m now points to cur (and unmarks m)
L08:    if (cur == NULL) {
L09: // No potential race with other prependers since *list_p is empty.
L10:      if (Atomic::cmpxchg(m, list_p, cur) == cur) {
L11: // Successfully switched *list_p to 'm'.
L12:        Atomic::inc(count_p);
L13:        break;
L14:      }
L15:      // Implied else: try it all again
L16:    } else {
L17:      // Try to mark next field to guard against races:
L18:      if (!mark_next(cur, &next)) {
L19:        continue;  // failed to mark next field so try it all again
L20:      }
L21:      // We marked the next field so try to switch *list_p to 'm'.
L22:      if (Atomic::cmpxchg(m, list_p, cur) != cur) {
L23:        // The list head has changed so unmark the next field and try again:
L24:        set_next(cur, next);
L25:        continue;
L26:      }
L27:      Atomic::inc(count_p);
L28:      set_next(cur, next);  // unmark next field
L29:      break;
L30:    }
L31:  }

...

ObjectMonitor 'm' is safely on the list at the point that we have updated 'list_p' to refer to 'm'. In this subsection's block of code, we also called two new functions, mark_next() and set_next(), that are explained in the next subsection.

Note: The above code snippet comes from prepend_to_common(); see that function for more context and a few more comments.

mark_next(), mark_om_ptr(), and set_next() Helper Functions

Managing marks on ObjectMonitors has been abstracted into a few helper functions. mark_next() is the first interesting one:

This is the v2.07 version of code and associated notes:

    L01:  while (true) {
    L02:    (void)mark_next_loop(m);  // mark m so we can safely update its next field
    L03:    ObjectMonitor* cur = NULL;
    L04 L01:  static bool mark_next(ObjectMonitor* om, ObjectMonitor** next_p) {
L02:    // Get current next field without any marking value.
L03:    ObjectMonitor* next = (ObjectMonitor*)
L04:        ((intptr_t)OrderAccess::load_acquire(&om->_next_om) & ~0x1);
L05NULL;
    L05:    // Mark the list head to guard against A-B-A race:
    L06:    if (Atomic::cmpxchg(mark_omlist_ptr(next)head(list_p, &om->_next_omcur, &next) != next) {
    L06L07:      return false;  // CouldList nothead markis thenow nextmarked fieldso orwe itcan wassafely alreadyswitch markedit.
    L08:      set_next(m, cur);  // L07:    } 
L08:    *next_p = next;
L09:    return true;
L10:  }

The above function tries to mark the next field in an ObjectMonitor:

m now points to cur (and unmarks m)
    L09:      OrderAccess::release_store(list_p, m);  // Switch list head to unmarked m.
    L10:      set_next(cur, next);  // Unmark the previous list head.
    L11:      break;
    L12:    }
    L13:    // The list is empty so try to set the list head.
    L14:    assert(cur == NULL, "cur must be NULL: cur=" INTPTR_FORMAT, p2i(cur));
    L15:    set_next(m, cur);  // m now points to NULL (and unmarks m)
    L16:    if (Atomic::cmpxchg(m, list_p, cur) == cur) {
    L17:      // List head is now unmarked m.
    L18:      break;
    L19:    }
    L20:    // Implied else: try it all again
    L21:  }
L22: Atomic::inc(count_p);

What the above block of code does is:

    • prepends an ObjectMonitor 'm' to the front of the list referred to by list_p
      • mark 'm's next field
      • mark the list head's next field
      • update 'm' to refer to the list head
      • update 'list_p' to refer to 'm'
      • unmark the next field in the previous list head
    • increments the counter referred to by 'count_p' by one

The above block of code can be called by multiple prependers in parallel or with deleters running in parallel and must not lose track of any ObjectMonitor. Of course, the "must not lose track of any ObjectMonitor" part is where all the details come in:

    • L02 loops to mark 'm's next field; we have to loop because another thread (T2) might have 'm' marked and we try again until we have marked it.
      You might be asking yourself: why does T2 have 'm' marked?
      • Before T1 was trying to prepend 'm' to an in-use list, T1 and T2 were racing to take an ObjectMonitor off the free list.
      • T1 won the race, marked 'm', removed 'm' from the free list and unmarked 'm'; T2 stalled before trying to mark 'm'.
      • T2 resumed and marked 'm', realized that 'm' was no longer the head of the free list, unmarked 'm' and tried it all again.
      • If our thread (T1) does not mark 'm' before it tries to prepend it to an in-use list, then T2's umarking of 'm' could erase the next value that T1 wants to put in 'm'.
    • L06 tries to mark the list head 'list_p'; if mark_list_head() returns true, we have the list head marked and can safely update it:
      • L08: Update 'm's next field to point to the current list head (which unmarks 'm').
      • L09: release-store 'm' into 'list_p' which switches the list head to an unmarked 'm'.
      • L10: We unmark the previous list head.
    • If mark_list_head() returned false, we have an empty list:
      • L15: Update 'm's next field to NULL (which unmarks 'm').
      • L16: Try to cmpxchg 'list_p' to 'm':
        • if cmpxchg works, then we're done.
        • Otherwise, another prepender won the race to update the list head so we have to try again.
    • The counter referred to by 'count_p' is incremented by one.

ObjectMonitor 'm' is safely on the list at the point that we have updated 'list_p' to refer to 'm'. In this subsection's block of code, we also called three new functions, mark_next_loop(), mark_list_head() and set_next(), that are explained in the next few subsections about helper functions.

Note: The above code snippet comes from prepend_to_common(); see that function for more context and a few more comments.

mark_next(), mark_om_ptr(), and set_next() Helper Functions

Managing marks on ObjectMonitors has been abstracted into a few helper functions. mark_next() is the first interesting one:

    L01:  static bool mark_next(ObjectMonitor* om, ObjectMonitor** next_p) {
L02:    // Get current next field without any marking value.
L03:    ObjectMonitor* next = (ObjectMonitor*)
L04:        ((intptr_t)OrderAccess::load_acquire(&om->_next_om) & ~0x1);
L05:    if (Atomic::cmpxchg(mark_om_ptr(next), &om->_next_om, next) != next) {
L06:      return false;  // Could not mark the next field or it was already marked.
L07:    } 
L08:    *next_p = next;
L09:    return true;
L10:  }

The above function tries to mark the next field in an ObjectMonitor:

    • If marking is successfulIf marking is successful, then the unmarked next field is returned via parameter and true is returned.
    • Otherwise, false is returned.

...

    • This function is simply a wrapper around a release-store of an ObjectMonitor* into the next field in an ObjectMonitor.
    • The typical "set_next(cur, next)" call sequence is easier to read than "OrderAccess::release_store(&cur→_next_om, next)".

Taking From The Start Of A List

    • than "OrderAccess::release_store(&cur→_next_om, next)".

mark_next_loop() Helper Function

mark_next_loop() is the next interesting helper function:

    L1:  static ObjectMonitor* mark_next_loop(ObjectMonitor* om) {
    L2:    ObjectMonitor* next;
    L3:    while (true) {
    L4:      if (mark_next(om, &next)) {
    L5:        // Marked om's next field so return the unmarked value.
    L6:        return next;
    L7:      }
    L8:    }
    L9:  }

The above function loops until it marks the next field of the target ObjectMonitor. The unmarked value of the next field is returned by the function. There is nothing particularly special about this function so we don't need any line specific annotations.

mark_list_head() Helper Function

mark_list_head() is the next interesting helper function:The next case to consider for lock-free list management with the Java Monitor subsystem is taking an ObjectMonitor from the start of a list. Taking an ObjectMonitor from the start of a list is a specialized form of delete that is guaranteed to interact with a thread that is prepending to the same list at the same time. Again, the core of the solution is to "mark" the next field in the ObjectMonitor at the head of the list we're trying to take the ObjectMonitor from, but we use slightly different code because we have less linkages to make than a prepend.

    L01:  static ObjectMonitor*bool takemark_from_start_of_commonlist_head(ObjectMonitor* volatile * list_p,
    L02:                                                                              int volatile ObjectMonitor** mid_p, ObjectMonitor** countnext_p) {
    L03:    while (true) {
    L04:      ObjectMonitor* nextmid = NULLOrderAccess::load_acquire(list_p);
    L04L05:         ObjectMonitor*if take(mid == NULL;) {
    L05:   L06:        return false;  // MarkThe thelist listis headempty toso guardnothing against A-B-A race:to mark.
    L07:      }
    L06L08:         if (!mark_list_head(list_pnext(mid, &take, &next)) {
    L07:      return NULL;  // None are available.
    L08:    }_p)) {
    L09:           // Switch marked list head to next (which unmarks theif (OrderAccess::load_acquire(list_p) != mid) {
    L10:          // The list head, but
    L10:    // leaves take marked):changed so we have to retry.
    L11:    OrderAccess::release_store(list_p, next);          set_next(mid, *next_p);  // unmark mid
    L12:    Atomic::dec(count_p)          continue;
    L13:    // Unmark take, but leave the next value for any lagging list        }
    L14:           // walkers.We Itmarked willnext getfield cleanedto upguard when take is prepended toagainst races.
    L15:    // the in-use list:        *mid_p = mid;
    L16:    set_next(take, next)        return true;
    L17:      }
    return take;L18:    }
    L18L19:  }

What the The above function does is:

...

tries to mark the

...

next field in the

...

list head's ObjectMonitor:

    • If the list is empty, false is returned.
    • Otherwise, the list head's ObjectMonitor* is returned via parameter (mid_p), the unmarked next field is returned via parameter (next_p) and true is returned.

The function can be called by more than one thread on the same 'list_p' at a time. False is only returned when 'list_p' refers to an empty list. Otherwise only one thread will return true at a time with the 'mid_p' and 'next_p' return parameters set. Since the next field in 'mid_p' is marked, any parallel callers to

...

mark_list_head() will loop until the

...

The function can be called by more than one thread at a time and each thread will take a unique ObjectMonitor from the start of the list (if one is available) without losing any other ObjectMonitors next field in the list head's ObjectMonitor is no longer marked. That typically happens when the list head's ObjectMonitor is taken off the list and 'list_p' is advanced to the next ObjectMonitor on the list. Of course, the "take a unique ObjectMonitor" and "without losing any other ObjectMonitors" parts are making sure that "only one thread will return true at a time" is where all the details come in:

    • L04 load-acquires the current 'list_p' value into 'mid'; the use of load-acquire is necessary to get the latest value release-stored or cmpxchg'ed by another thread.
    • L0[56] is the empty list check and the only time that false is returned by this function.
    • L08 L03 tries to mark the list head:
      • mark_list_head() returns false if the list is empty so we return NULL on L07.
      • Otherwise, 'take' is a pointer to the marked list head and 'next' is the unmarked next field in the list head.
    • L11 release-stores 'next' into 'list_p'.
      You might be asking yourself: Why release-store instead of cmpxchg?
      • mark_list_head() only returns to the caller when it has marked the next field in the ObjectMonitor at the head of the list.
      • Because of that guarantee, any prepender or deleter thread that is running in parallel must loop until we have release-stored 'next' into 'list_p' which unmarks the list head.
    • L12 decrements the counter referred to by 'count_p'.
    • L16 unmarks 'take' using the unmarked 'next' value we got from mark_list_head():
      • Keeping the 'next' value in take's next field allows any lagging list walker to get to the next ObjectMonitor on that list.
      • take's next field will get cleaned up when take is prepended to its target in-use list.
    • L17 returns 'take' to the caller.

The take_from_start_of_common() function calls another helper function, mark_list_head(), that is explained in the next subsection.

mark_list_head() Helper Function

    • next field in 'mid':
      • If marking is not successful, we loop around to try it all again.
      • If marking is successful, then 'next_p' contains mid's unmarked next field value.
      • L09 load-acquires the current 'list_p' value to see if it still matches 'mid':
        • If the list head has changed, then we unmark mid on L11 and try it all again.
        • Otherwise, 'mid' is returned via 'mid_p' and we return true.

When this function returns true, the next field in 'mid_p' is marked and any parallel callers of mark_list_head() on the same list will be looping until the next field in the list head's ObjectMonitor is no longer marked. The caller that just got the 'true' return needs to finish up its work with 'mid_p' quickly.

Taking From The Start Of A List

The next case to consider for lock-free list management with the Java Monitor subsystem is taking an ObjectMonitor from the start of a list. Taking an ObjectMonitor from the start of a list is a specialized form of delete that is guaranteed to interact with a thread that is prepending to the same list at the same time. Again, the core of the solution is to "mark" the next field in the ObjectMonitor at the head of the list we're trying to take the ObjectMonitor from, but we use slightly different code because we have less linkages to make than a prepend.mark_list_head() is the next interesting helper function:

    L01:  static bool mark_list_headObjectMonitor* take_from_start_of_common(ObjectMonitor* volatile * list_p,
    L02:                                                                              ObjectMonitor** mid_p, ObjectMonitor*int volatile * nextcount_p) {
    L03:    ObjectMonitor* whilenext (true) {= NULL;
    L04:         ObjectMonitor* midtake = NULL;
    OrderAccess::load_acquire(list_p);
    L05:      if (mid == NULLL05:    // Mark the list head to guard against A-B-A race:
    L06:    if (!mark_list_head(list_p, &take, &next)) {
    L06L07:             return falseNULL;  // TheNone list is empty so nothing to markare available.
    L07L08:         }
    L08:      if (mark_next(mid, next_p)) {
    L09:        if (OrderAccess::load_acquire(list_p) != mid) {L09:    // Switch marked list head to next (which unmarks the list head, but
    L10:             // Theleaves list head changed so we have to retry.take marked):
    L11:          set_next(mid, *next_p);  // unmark mid
    L12:          continue;
    L13:        }    OrderAccess::release_store(list_p, next);
    L12:    Atomic::dec(count_p);
    L13:    // Unmark take, but leave the next value for any lagging list
    L14:           // We marked next field to guard against races. walkers. It will get cleaned up when take is prepended to
    L15:        *mid_p = mid;    // the in-use list:
    L16:        return true    set_next(take, next);
    L17:      }
    L18:    }return take;
    L19L18:   }

The What the above function tries does is:

    • Tries to mark the

...

    • ObjectMonitor at the

...

    • If the list is empty, false is returned.
    • Otherwise, the list head's ObjectMonitor* is returned via parameter (mid_p), the unmarked next field is returned via parameter (next_p) and true is returned.

...

    • head of the list:
      • Marking will only fail if the list is empty so that NULL can be returned.
      • Otherwise mark_list_head() will loop until the

...

      • ObjectMonitor at the list head

...

      • has been marked.

...

    • Updates the list head to refer to the next ObjectMonitor.
    • Decrements the counter referred to by 'count_p'.
    • Unmarks the next field in the taken ObjectMonitor.

The function can be called by more than one thread at a time and each thread will take a unique ObjectMonitor from the start of the list (if one is available) without losing any other ObjectMonitors 's ObjectMonitor is taken off the list and 'list_p' is advanced to the next ObjectMonitor on the list. Of course, making sure that "only one thread will return true at a time" is the "take a unique ObjectMonitor" and "without losing any other ObjectMonitors" parts are where all the details come in:

    • L04 load-acquires the current 'list_p' value into 'mid'; the use of load-acquire is necessary to get the latest value release-stored or cmpxchg'ed by another thread.
    • L0[56] is the empty list check and the only time that false is returned by this function.
    • L08 tries to mark the next field in 'mid':
      • If marking is not successful, we loop around to try it all again.
      • If marking is successful, then 'next_p' contains mid's unmarked next field value.
      • L09 load-acquires the current 'list_p' value to see if it still matches 'mid':
        • If the list head has changed, then we unmark mid on L11 and try it all again.
        • Otherwise, 'mid' is returned via 'mid_p' and we return true.

...

    • L03 tries to mark the list head:
      • mark_list_head() returns false if the list is empty so we return NULL on L07.
      • Otherwise, 'take' is a pointer to the marked list head and 'next' is the unmarked next field in the list head.
    • L11 release-stores 'next' into 'list_p'.
      You might be asking yourself: Why release-store instead of cmpxchg?
      • mark_list_head() only returns to the caller when it has marked the next field in the ObjectMonitor at the head of the list.
      • Because of that guarantee, any prepender or deleter thread that is running in parallel must loop until we have release-stored 'next' into 'list_p' which unmarks the list head.
    • L12 decrements the counter referred to by 'count_p'.
    • L16 unmarks 'take' using the unmarked 'next' value we got from mark_list_head()

...

    • :
      • Keeping the 'next' value in take's next field allows any lagging list walker to get to the next ObjectMonitor on that list.
      • take's next field will get cleaned up when take is prepended to its target in-use list.
    • L17 returns 'take' to the caller.

Using The New Lock-Free Monitor List Functions

...

    L01:  if (from_per_thread_alloc) {
    L02:    mark_list_head(&self->om_in_use_list, &mid, &next);
    L03:    while (true) {
    L04:      if (m == mid) {
    L05:        if (Atomic::cmpxchg(next, &self->om_in_use_list, mid) != mid) {
    L06:          ObjectMonitor* marked_mid = mark_om_ptr(mid);
    L07:          Atomic::cmpxchg(next, &cur_mid_in_use->_next_om, marked_mid);
    L08:        }
    L09:        extracted = true;
    L10:        Atomic::dec(&self->om_in_use_count);
    L11:        set_next(mid, next);
    L12:        break;
    L13:      }
    L14:      if (cur_mid_in_use != NULL) {
    L15:        set_next(cur_mid_in_use, mid);  // umark cur_mid_in_use
    L16:      }
    L17:      cur_mid_in_use = mid;
    L18:      mid = next;
    L19:      next = mark_next_loop(mid);
    L20:    }
    L21:  }
    L22:  prepend_to_om_free_list(self, m);self, m);

Note: In v2.07, I figured out a simpler way to do L05-L08:

    L05:        if (cur_mid_in_use == NULL) {
    L06:          OrderAccess::release_store(&self->om_in_use_list, next);
    L07:        } else {
    L08:          OrderAccess::release_store(&cur_mid_in_use->_next_om, next);
    L09:        }

The line numbers in the analysis below are still for the v2.06 version and will be updated when we roll the project forward to v2.07.

Most of the above code block extracts 'm' from self's in-use list; it is not an exact quote from om_release(), but it is the highlights:

    • L02 is used mark self's in-use list head:
      • 'mid' is self's in-use list head and its next field is marked.
      • 'next' is the unmarked next field from 'mid'.
    • L03 → L20: self's in-use list is traversed looking for the target ObjectMonitor 'm':
      • L04: if the current 'mid' matches 'm':
        • L05: if we can't cmpxchg self's in-use list head to the 'next' ObjectMonitor*:
          Note: This cmpxchg only works if 'm' is self's in-use list head and no-harm-no-foul if 'm' is not self's in-use list head. This is faster than doing a load-acquire of self's in-use list head, checking the value and then calling cmpxchg.
          • L06: make a marked copy of 'mid'
          • L07: we cmpxchg cur_mid_in_use's next field from 'marked_mid' to 'next'.
            Note: We use cmpxchg here instead of release-store so that we can sanity check the result; see the real code.
        • L09 → L12: we've successfully extracted 'm' from self's in-use list so we decrement self's in-use counter, unmark the next field in 'mid' and we're done.
      • L1[45]: if cur_mid_in_use != NULL, then unmark its next field.
      • L17: set 'cur_mid_in_use' to 'mid'
        Note: cur_mid_in_use keeps the marked next field so that it remains stable for a possible next field change. It cannot be deflated while it is marked.
      • L18: set 'mid' to 'next'.
      • L19: mark next field in the new 'mid' and update 'next'; loop around and do it all again.

The last line of the code block (L22) prepends 'm' to self's free list.

mark_next_loop() Helper Function

mark_next_loop() is the next interesting helper function:

    L1:  static ObjectMonitor* mark_next_loop(ObjectMonitor* om) {
    L2:    ObjectMonitor* next;
    L3:    while (true) {
    L4:      if (mark_next(om, &next)) {
    L5:        // Marked om's next field so return the unmarked value.
    L6:        return next;
    L7:      }
    L8:    }
    L9:  }
          • 's next field from 'marked_mid' to 'next'.
            Note: We use cmpxchg here instead of release-store so that we can sanity check the result; see the real code.
        • L09 → L12: we've successfully extracted 'm' from self's in-use list so we decrement self's in-use counter, unmark the next field in 'mid' and we're done.
      • L1[45]: if cur_mid_in_use != NULL, then unmark its next field.
      • L17: set 'cur_mid_in_use' to 'mid'
        Note: cur_mid_in_use keeps the marked next field so that it remains stable for a possible next field change. It cannot be deflated while it is marked.
      • L18: set 'mid' to 'next'.
      • L19: mark next field in the new 'mid' and update 'next'; loop around and do it all again.

The last line of the code block (L22) prepends 'm' to self's free listThe above function loops until it marks the next field of the target ObjectMonitor. The unmarked value of the next field is returned by the function. There is nothing particularly special about this function so we don't need any line specific annotations.

ObjectSynchronizer::om_flush(Thread* self)

...

    L01:  int ObjectSynchronizer::deflate_monitor_list(ObjectMonitor* volatile * list_p,
    L02:                                               int volatile * count_p,
    L03:                                               ObjectMonitor** free_head_p,
    L04:                                               ObjectMonitor** free_tail_p) {
    L05:    ObjectMonitor* cur_mid_in_use = NULL;
    L06:    ObjectMonitor* mid = NULL;
    L07:    ObjectMonitor* next = NULL;
    L08:    int deflated_count = 0;
    L09:    if (!mark_list_head(list_p, &mid, &next)) {
    L10:      return 0;  // The list is empty so nothing to deflate.
    L11:    }
    L12:    while (true) {
    L13:      oop obj = (oop) mid->object();
    L14:      if (obj != NULL && deflate_monitor(mid, obj, free_head_p, free_tail_p)) {
    L15:        if (Atomic::cmpxchg(next, list_p, mid) != mid) {
    L16:          Atomic::cmpxchg(next, &cur_mid_in_use->_next_om, mid);
    L17:        }
    L18:        deflated_count++;
    L19:        Atomic::dec(count_p);
    L20:        set_next(mid, NULL);
    L21:        mid = next;
    L22:      } else {
    L23:        set_next(mid, next);  // unmark next field
    L24:        cur_mid_in_use = mid;
    L25:        mid = next;
    L26:      }
    L27:      if (mid == NULL) {
    L28:        break;  // Reached end of the list so nothing more to deflate.
    L29:      }
    L30:      next = mark_next_loop(mid);
    L31:    }
    L32:    return deflated_count;
    L33:  } }

Note: In v2.07, I figured out a simpler way to do L15-L17:

    L15:       if (cur_mid_in_use == NULL) {
    L16:         OrderAccess::release_store(list_p, next);
    L17:       } else {
    L18:         OrderAccess::release_store(&cur_mid_in_use->_next_om, next);
    L19:       }

The line numbers in the analysis below are still for the v2.06 version and will be updated when we roll the project forward to v2.07.

The above is not an exact copy of the code block from deflate_monitor_list(), but it is the highlights. What the above code block needs to do is pretty simple:

...

    L01:  int ObjectSynchronizer::deflate_monitor_list_using_JT(ObjectMonitor* volatile * list_p,
    L02:                                                        int volatile * count_p,
    L03:                                                        ObjectMonitor** free_head_p,
    L04:                                                        ObjectMonitor** free_tail_p,
    L05:                                                        ObjectMonitor** saved_mid_in_use_p) {
    L06:    ObjectMonitor* cur_mid_in_use = NULL;
    L07:    ObjectMonitor* mid = NULL;
    L08:    ObjectMonitor* next = NULL;
    L09:    ObjectMonitor* next_next = NULL;
    L10:    int deflated_count = 0;
    L11:    if (*saved_mid_in_use_p == NULL) {
    L12:      if (!mark_list_head(list_p, &mid, &next)) {
    L13:        return 0;  // The list is empty so nothing to deflate.
    L14:      }
    L15:    } else {
    L16:      cur_mid_in_use = *saved_mid_in_use_p;
    L17:      mid = mark_next_loop(cur_mid_in_use);
    L18:      if (mid == NULL) {
    L19:        set_next(cur_mid_in_use, NULL);  // unmark next field
    L20:        *saved_mid_in_use_p = NULL;
    L21:        return 0;  // The remainder is empty so nothing more to deflate.
    L22:      }
    L23:      next = mark_next_loop(mid);
    L24:    }
    L25:    while (true) {
    L26:      if (next != NULL) {
    L27:        next_next = mark_next_loop(next);
    L28:      }
    L29:      if (mid->object() != NULL && mid->is_old() &&
    L30:          deflate_monitor_using_JT(mid, free_head_p, free_tail_p)) {
    L31:        if (Atomic::cmpxchg(next, list_p, mid) != mid) {
    L32:          ObjectMonitor* marked_mid = mark_om_ptr(mid);
    L33:          ObjectMonitor* marked_next = mark_om_ptr(next);
    L34:          Atomic::cmpxchg(marked_next, &cur_mid_in_use->_next_om, marked_mid);
    L35:        }
    L36:        deflated_count++;
    L37:        Atomic::dec(count_p);
    L38:        set_next(mid, NULL);
    L39:        mid = next;  // mid keeps non-NULL next's marked next field
    L40:        next = next_next;
    L41:      } else {
    L42:        if (cur_mid_in_use != NULL) {
    L43:          set_next(cur_mid_in_use, mid);  // umark cur_mid_in_use
    L44:        }
    L45:        cur_mid_in_use = mid;
    L46:        mid = next;  // mid keeps non-NULL next's marked next field
    L47:        next = next_next;
    L48:        if (SafepointSynchronize::is_synchronizing() &&
    L49:            cur_mid_in_use != OrderAccess::load_acquire(list_p) &&
    L50:            cur_mid_in_use->is_old()) {
    L51:          *saved_mid_in_use_p = cur_mid_in_use;
    L52:          set_next(cur_mid_in_use, mid);  // umark cur_mid_in_use
    L53:          if (mid != NULL) {
    L54:            set_next(mid, next);  // umark mid
    L55:          }
    L56:          return deflated_count;
    L57:        }
    L58:      }
    L59:      if (mid == NULL) {
    L60:        if (cur_mid_in_use != NULL) {
    L61:          set_next(cur_mid_in_use, mid);  // umark cur_mid_in_use
    L62:        }
    L63:        break;  // Reached end of the list so nothing more to deflate.
    L64:      }
    L65:    }
    L66:    *saved_mid_in_use_p = NULL;
    L67:    return deflated_count;
    L68:  }

Note: In v2.07, I figured out a simpler way to do L31-L35:

    L31:        if (cur_mid_in_use == NULL) {
    L32:          OrderAccess::release_store(list_p, next);
    L33:        } else {
    L34:          ObjectMonitor* marked_next = mark_om_ptr(next);
    L35:          OrderAccess::release_store(&cur_mid_in_use->_next_om, marked_next);
    L36:        }

The line numbers in the analysis below are still for the v2.06 version and will be updated when we roll the project forward to v2.07.

The above is not an exact copy of the code block from deflate_monitor_list_using_JT(), but it is the highlights. What the above code block needs to do is pretty simple:

...