[rvm-research] Initialization of reference fields in Poisoned collector

Erik Brangs <[email protected]> Sun, 9 Apr 2017 14:19:54 +0200
Newsgroups gmane.comp.java.jikes.rvm.devel
Message-ID <[email protected]>
Hi,

it seems to me that the Poisoned collector doesn't initialize null fields properly. When initializing the memory for an object, all the memory is initialized to 0. That's incorrect for Poisoned because that collector poisons all references. So a null reference in an object actually has the (poisoned) value of 1. This leads to problems for compare-and-swap operations: a compare-and-swap operation will compare the correctly poisoned value of null (i.e. 1) with the non-poisoned value in the initialized object field (i.e. 0).

I've attached a patch that seems to fix this. It makes the recently added TestSynchronization pass on BaseBasePoisoned.

I'd appreciate any reviews of the patch.


Kind regards,

Erik Brangs

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

_______________________________________________
Jikesrvm-researchers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jikesrvm-researchers
initializeFieldsForPoisoned.diff (text/x-patch, 5.9 KB)
commit 6d0670ddf05ed8398e3bcd51934620e78224cc23
Author: Erik Brangs <[email protected]>
Date:   Sun Apr 9 12:20:44 2017 +0200

    Rough draft of a fix for the Poisoned collector problem.

diff --git a/MMTk/ext/vm/jikesrvm/org/jikesrvm/mm/mmtk/Barriers.java b/MMTk/ext/vm/jikesrvm/org/jikesrvm/mm/mmtk/Barriers.java
index 53b90d7..f20bceb 100644
--- a/MMTk/ext/vm/jikesrvm/org/jikesrvm/mm/mmtk/Barriers.java
+++ b/MMTk/ext/vm/jikesrvm/org/jikesrvm/mm/mmtk/Barriers.java
@@ -13,7 +13,12 @@
 package org.jikesrvm.mm.mmtk;
 
 import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
+import static org.mmtk.utility.Constants.ARRAY_ELEMENT;
+import static org.mmtk.utility.Constants.INSTANCE_FIELD;
 
+import org.jikesrvm.classloader.RVMType;
+import org.jikesrvm.objectmodel.ObjectModel;
+import org.jikesrvm.objectmodel.TIB;
 import org.jikesrvm.runtime.Magic;
 import org.mmtk.vm.VM;
 
@@ -313,6 +318,55 @@ public class Barriers extends org.mmtk.vm.Barriers {
   }
 
   /**
+   * Initializes the reference fields of a given object reference. Most collectors
+   * won't need to use this method.
+   * <p>
+   * The Poisoned collector uses this method. Without using this method,
+   * all reference fields would be initialized to {@code Word.zero()}. However,
+   * the value for {@code null} in the Poisoned collector is actually
+   * {@code Poisoned.poison(ObjectReference.fromObject(null))}, which is
+   * {@code Word.one()} at the time of this writing. Not initializing the value
+   * to the real value of {@code null} leads to problems when the value isn't
+   * explicitly initialized before it is read (i.e. when the programmer relies
+   * on the JVM to initialize the value to {@code null}). This can occur when
+   * using intrinsics such as compare-and-swap.
+   *
+   * @param ref the object that has the reference field(s)
+   * @param tibAddr the object's type TIB
+   */
+  @Override
+  public final void initializeObjectReferenceFields(ObjectReference ref,
+      ObjectReference tibAddr) {
+    ObjectReference nullValue = Word.zero().toAddress().toObjectReference();
+    // location is actually unknown. I suppose we could try mapping the
+    // reference offsets to field offsets if we really needed it.
+    Word location = Word.zero();
+
+    TIB tib = Magic.addressAsTIB(tibAddr.toAddress());
+    RVMType type = tib.getType();
+    int[] referenceOffsets = type.getReferenceOffsets();
+
+    if (referenceOffsets == RVMType.REFARRAY_OFFSET_ARRAY) {
+      if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(type.isArrayType() &&
+          type.asArray().getElementType().isReferenceType());
+      int arrayLength = ObjectModel.getArrayLength(ref.toObject());
+      for (int i = 0; i < arrayLength; i++) {
+        Word offset = Offset.fromIntSignExtend(i << LOG_BYTES_IN_ADDRESS).toWord();
+        Address slotAddress = ref.toAddress().plus(i << LOG_BYTES_IN_ADDRESS);
+        VM.activePlan.mutator().objectReferenceWrite(ref, slotAddress, nullValue, offset, location, ARRAY_ELEMENT);
+      }
+    } else {
+      if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(type.isClassType() ||
+          (type.isArrayType() && !type.asArray().getElementType().isReferenceType()));
+      for (int i = 0; i < referenceOffsets.length; i++) {
+        Word offset = Offset.fromIntSignExtend(referenceOffsets[i]).toWord();
+        Address slotAddress = ref.toAddress().plus(referenceOffsets[i]);
+        VM.activePlan.mutator().objectReferenceWrite(ref, slotAddress, nullValue, offset, location, INSTANCE_FIELD);
+      }
+    }
+  }
+
+  /**
    * Perform the actual write of an object reference write barrier.
    *
    * @param objref The object that has the reference field
diff --git a/MMTk/src/org/mmtk/plan/poisoned/PoisonedMutator.java b/MMTk/src/org/mmtk/plan/poisoned/PoisonedMutator.java
index 11b646f..fbb9956 100644
--- a/MMTk/src/org/mmtk/plan/poisoned/PoisonedMutator.java
+++ b/MMTk/src/org/mmtk/plan/poisoned/PoisonedMutator.java
@@ -27,6 +27,13 @@ import org.vmmagic.unboxed.Word;
 @Uninterruptible
 public class PoisonedMutator extends MSMutator {
 
+  @Override
+  public void postAlloc(ObjectReference ref, ObjectReference typeRef, int bytes,
+      int allocator) {
+    VM.barriers.initializeObjectReferenceFields(ref, typeRef);
+    super.postAlloc(ref, typeRef, bytes, allocator);
+  }
+
   /****************************************************************************
    *
    * Write and read barriers. By default do nothing, override if
diff --git a/MMTk/src/org/mmtk/utility/Constants.java b/MMTk/src/org/mmtk/utility/Constants.java
index 7130c7d..29cc3f4 100644
--- a/MMTk/src/org/mmtk/utility/Constants.java
+++ b/MMTk/src/org/mmtk/utility/Constants.java
@@ -35,7 +35,7 @@ public final class Constants {
    */
 
   /**
-   *
+   * Modes.
    */
   public static final int INSTANCE_FIELD = 0;
   public static final int ARRAY_ELEMENT = 1;
diff --git a/MMTk/src/org/mmtk/vm/Barriers.java b/MMTk/src/org/mmtk/vm/Barriers.java
index 0ebe0dc..a915c77 100644
--- a/MMTk/src/org/mmtk/vm/Barriers.java
+++ b/MMTk/src/org/mmtk/vm/Barriers.java
@@ -221,6 +221,20 @@ public abstract class Barriers {
   public abstract double doubleRead(ObjectReference ref, Word metaDataA, Word metaDataB, int mode);
 
   /**
+   * Initializes the reference fields of a given object reference. Most collectors
+   * won't need to use this method.
+   * <p>
+   * It is only required if the default value for a reference field is not the
+   * default value that's provided by the initialization for the memory of the
+   * object (e.g. if memory is initialized to zero and the default value for a
+   * reference is not zero).
+   *
+   * @param ref the object that has the reference field
+   * @param typeRef the object's type reference
+   */
+  public abstract void initializeObjectReferenceFields(ObjectReference ref, ObjectReference typeRef);
+
+  /**
    * Perform the actual write of an object reference write barrier.
    *
    * @param ref The object that has the reference field