- Loading...
...
Note: The above code snippet comes from ObjectSynchronizer::prepend_block_to_lists(); see that function for more complete context (and comments).
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.
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: }
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 does not lose track of any ObjectMonitor. Of course, the "does 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 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:
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:
The function can be called by multiple threads at the same time and only one thread will succeed in the marking operation (return == true) and all other threads will get return == false. Of course, the "only one thread will succeed" part is where all the details come in:
The mark_next() function calls another helper function, mark_om_ptr(), that needs a quick explanation:
L1: static ObjectMonitor* mark_om_ptr(ObjectMonitor* om) {
L2: return (ObjectMonitor*)((intptr_t)om | 0x1);
L3: }
This function encapsulates the setting of the marking bit in an ObjectMonitor* for the purpose of hiding the details and making the calling code easier to read:
set_next() is the next interesting function and it also only needs a quick explanation:
L1: static void set_next(ObjectMonitor* om, ObjectMonitor* value) {
L2: OrderAccess::release_store(&om->_next_om, value);
L3: }
This function encapsulates the setting of the next field in an ObjectMonitor for the purpose of hiding the details and making the calling code easier to read:
...