8225357: Rewire ShenandoahHeap::maybe_update_with_forwarded for contending fixups
authorshade
Fri, 07 Jun 2019 11:47:53 +0200
changeset 55286 55319b27b346
parent 55285 9a120214e732
child 55287 09b09388f197
8225357: Rewire ShenandoahHeap::maybe_update_with_forwarded for contending fixups Reviewed-by: rkennke
src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp
src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp	Fri Jun 07 11:19:34 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.inline.hpp	Fri Jun 07 11:47:53 2019 +0200
@@ -253,9 +253,12 @@
       ShouldNotReachHere();
     }
 
-    // Note: Only when concurrently updating references can obj become NULL here.
-    // It happens when a mutator thread beats us by writing another value. In that
-    // case we don't need to do anything else.
+    // Note: Only when concurrently updating references can obj be different
+    // (that is, really different, not just different from-/to-space copies of the same)
+    // from the one we originally loaded. Mutator thread can beat us by writing something
+    // else into the location. In that case, we would mark through that updated value,
+    // on the off-chance it is not handled by other means (e.g. via SATB). However,
+    // if that write was NULL, we don't need to do anything else.
     if (UPDATE_REFS != CONCURRENT || !CompressedOops::is_null(obj)) {
       shenandoah_assert_not_forwarded(p, obj);
       shenandoah_assert_not_in_cset_except(p, obj, heap->cancelled_gc());
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp	Fri Jun 07 11:19:34 2019 +0200
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp	Fri Jun 07 11:47:53 2019 +0200
@@ -152,24 +152,29 @@
     }
 
     shenandoah_assert_forwarded_except(p, heap_oop, is_full_gc_in_progress() || is_degenerated_gc_in_progress());
+    shenandoah_assert_not_forwarded(p, forwarded_oop);
     shenandoah_assert_not_in_cset_except(p, forwarded_oop, cancelled_gc());
 
     // If this fails, another thread wrote to p before us, it will be logged in SATB and the
     // reference be updated later.
-    oop result = atomic_compare_exchange_oop(forwarded_oop, p, heap_oop);
+    oop witness = atomic_compare_exchange_oop(forwarded_oop, p, heap_oop);
 
-    if (oopDesc::equals_raw(result, heap_oop)) { // CAS successful.
-      return forwarded_oop;
+    if (!oopDesc::equals_raw(witness, heap_oop)) {
+      // CAS failed, someone had beat us to it. Normally, we would return the failure witness,
+      // because that would be the proper write of to-space object, enforced by strong barriers.
+      // However, there is a corner case with arraycopy. It can happen that a Java thread
+      // beats us with an arraycopy, which first copies the array, which potentially contains
+      // from-space refs, and only afterwards updates all from-space refs to to-space refs,
+      // which leaves a short window where the new array elements can be from-space.
+      // In this case, we can just resolve the result again. As we resolve, we need to consider
+      // the contended write might have been NULL.
+      oop result = ShenandoahBarrierSet::resolve_forwarded(witness);
+      shenandoah_assert_not_forwarded_except(p, result, (result == NULL));
+      shenandoah_assert_not_in_cset_except(p, result, (result == NULL) || cancelled_gc());
+      return result;
     } else {
-      // Note: we used to assert the following here. This doesn't work because sometimes, during
-      // marking/updating-refs, it can happen that a Java thread beats us with an arraycopy,
-      // which first copies the array, which potentially contains from-space refs, and only afterwards
-      // updates all from-space refs to to-space refs, which leaves a short window where the new array
-      // elements can be from-space.
-      // assert(CompressedOops::is_null(result) ||
-      //        oopDesc::equals_raw(result, ShenandoahBarrierSet::resolve_oop_static_not_null(result)),
-      //       "expect not forwarded");
-      return NULL;
+      // Success! We have updated with known to-space copy. We have already asserted it is sane.
+      return forwarded_oop;
     }
   } else {
     shenandoah_assert_not_forwarded(p, heap_oop);