Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Add om_flush() subsection; a minor typo edit in passing.

...

mark_next_loop() Helper Function

mark_next_loop() is the next interesting helper function:

...

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.

ObjectSynchronizer::om_flush(Thread* self)

ObjectSynchronizer::om_flush() is reponsible for flushing self's in-use list to the global in-use list and self's free list to the global free list during self's thread exit processing. om_flush() starts with self's in-use list:

    L01:  if (mark_list_head(&self->om_in_use_list, &in_use_list, &next)) {
    L02:    in_use_tail = in_use_list;
    L03:    in_use_count++;
    L04:    for (ObjectMonitor* cur_om = unmarked_next(in_use_list); cur_om != NULL;) {
    L05:      if (is_next_marked(cur_om)) {
    L06:        while (is_next_marked(cur_om)) {
    L07:          os::naked_short_sleep(1);
    L08:        }
    L09:        cur_om = unmarked_next(in_use_tail);
    L10:        continue;
    L11:      }
    L12:      if (!cur_om->is_active()) {
    L13:        cur_om = unmarked_next(in_use_tail);
    L14:        continue;
    L15:      }
    L16:      in_use_tail = cur_om;
    L17:      in_use_count++;
    L18:      cur_om = unmarked_next(cur_om);
    L19:    }
    L20:    OrderAccess::release_store(&self->om_in_use_count, 0);
    L21:    OrderAccess::release_store(&self->om_in_use_list, (ObjectMonitor*)NULL);
    L22:    set_next(in_use_list, next);
    L23:  }

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

    • Count the number of ObjectMonitors on the in-use list using 'in_use_count'.
    • Point to the last ObjectMonitor on the in-use list using 'in_use_tail'.
    • Set self's in-use count to zero.
    • Set self's in-use llist to NULL.

However, in this case, there are a lot of details:

    • L01 marks the in-use list head (if it is not empty):
      • 'in_use_list' is self's in-use list head and its next field is marked.
      • 'next' is the unmarked next field from 'in_use_list'.
      • The in-use list head is kept marked to prevent an async deflation thread from entering the list behind this thread.
        Note: An async deflation thread does check to see if the target thread is exiting, but if it has made it past that check before this thread started exiting, then we're racing.
    • L04-L18: loops over the in-use list counting and advancing 'in_use_tail'.
      • L05-L10: 'cur_om's next field is marked so there must be an async deflater thread ahead of us so we delay to give it a chance to finish and refetch 'in_use_tail's (possibly changed) next field and try again.
      • L12-L14: 'cur_om' was deflated and its allocation state was changed to Free while it was marked. We just happened to be lucky enough to see it just after it was unmarked (and added to the free list). We refetch 'in_use_tail's (possibly changed) next field and try again.
      • L1[67]: finally 'cur_om' has been completely vetted so we can update 'in_use_tail' and increment 'in_use_count'.
      • L18: advance 'cur_om' to the next ObjectMonitor and do it all again.
    • L20: release-store self's in-use count to zero.
      Note: We clear self's in-use count before umarking self's in-use list head to avoid races.
    • L21: release-store self's in-use list head to NULL.
    • L22: unmark the disconnected list head.
      Note: Yes, the next field in self's in-use list head was kept marked for the whole loop to keep any racing async deflater thread out of the in-use list. After L21, the racing async deflater thread will loop around and see self's in-use list is empty and bail out.

The code to process self's free list is much, much simpler because we don't have any races with an async deflater thread like self's in-use list. The only interesting bits:

    • load-acquire self's free list head.
    • release-store self's free list head to NULL.
    • release-store self's free list count to zero.

The last interesting bits for this function are prepending the local lists to the right global places:

    • prepend_list_to_g_free_list(free_list, free_tail, free_count);
    • prepend_list_to_g_om_in_use_list(in_use_list, in_use_tail, in_use_count);

Housekeeping Parts of the Algorithm

...