- Loading...
...
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 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.
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:
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:
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.
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:
...
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() 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:
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:
The take_from_start_of_common() function calls another helper function, mark_list_head(), that is explained in the next subsection.
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.
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:
...
...
...
...
...
...
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:
...
...
...
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:
The last line of the code block (L22) prepends 'm' to self's free list.
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 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.
...
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:
...