8133348: Reference.reachabilityFence
authorpsandoz
Tue, 06 Oct 2015 18:42:06 +0200
changeset 35254 ba6a26f39b86
parent 35253 ed92ce9fac46
child 35255 6f0bf592d149
8133348: Reference.reachabilityFence Reviewed-by: plevart, mr, chegar, mchung Contributed-by: dl@cs.oswego.edu, aleksey.shipilev@oracle.com, paul.sandoz@oracle.com
jdk/src/java.base/share/classes/java/lang/ref/Reference.java
jdk/test/java/lang/ref/ReachabilityFenceTest.java
--- a/jdk/src/java.base/share/classes/java/lang/ref/Reference.java	Wed Dec 09 15:26:52 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/lang/ref/Reference.java	Tue Oct 06 18:42:06 2015 +0200
@@ -25,6 +25,7 @@
 
 package java.lang.ref;
 
+import jdk.internal.vm.annotation.DontInline;
 import sun.misc.Cleaner;
 import jdk.internal.HotSpotIntrinsicCandidate;
 import jdk.internal.misc.JavaLangRefAccess;
@@ -311,4 +312,120 @@
         this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
     }
 
+    /**
+     * Ensures that the object referenced by the given reference remains
+     * <a href="package-summary.html#reachability"><em>strongly reachable</em></a>,
+     * regardless of any prior actions of the program that might otherwise cause
+     * the object to become unreachable; thus, the referenced object is not
+     * reclaimable by garbage collection at least until after the invocation of
+     * this method.  Invocation of this method does not itself initiate garbage
+     * collection or finalization.
+     *
+     * <p> This method establishes an ordering for
+     * <a href="package-summary.html#reachability"><em>strong reachability</em></a>
+     * with respect to garbage collection.  It controls relations that are
+     * otherwise only implicit in a program -- the reachability conditions
+     * triggering garbage collection.  This method is designed for use in
+     * uncommon situations of premature finalization where using
+     * {@code synchronized} blocks or methods, or using other synchronization
+     * facilities are not possible or do not provide the desired control.  This
+     * method is applicable only when reclamation may have visible effects,
+     * which is possible for objects with finalizers (See
+     * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.6">
+     * Section 12.6 17 of <cite>The Java&trade; Language Specification</cite></a>)
+     * that are implemented in ways that rely on ordering control for correctness.
+     *
+     * @apiNote
+     * Finalization may occur whenever the virtual machine detects that no
+     * reference to an object will ever be stored in the heap: The garbage
+     * collector may reclaim an object even if the fields of that object are
+     * still in use, so long as the object has otherwise become unreachable.
+     * This may have surprising and undesirable effects in cases such as the
+     * following example in which the bookkeeping associated with a class is
+     * managed through array indices.  Here, method {@code action} uses a
+     * {@code reachabilityFence} to ensure that the {@code Resource} object is
+     * not reclaimed before bookkeeping on an associated
+     * {@code ExternalResource} has been performed; in particular here, to
+     * ensure that the array slot holding the {@code ExternalResource} is not
+     * nulled out in method {@link Object#finalize}, which may otherwise run
+     * concurrently.
+     *
+     * <pre> {@code
+     * class Resource {
+     *   private static ExternalResource[] externalResourceArray = ...
+     *
+     *   int myIndex;
+     *   Resource(...) {
+     *     myIndex = ...
+     *     externalResourceArray[myIndex] = ...;
+     *     ...
+     *   }
+     *   protected void finalize() {
+     *     externalResourceArray[myIndex] = null;
+     *     ...
+     *   }
+     *   public void action() {
+     *     try {
+     *       // ...
+     *       int i = myIndex;
+     *       Resource.update(externalResourceArray[i]);
+     *     } finally {
+     *       Reference.reachabilityFence(this);
+     *     }
+     *   }
+     *   private static void update(ExternalResource ext) {
+     *     ext.status = ...;
+     *   }
+     * }}</pre>
+     *
+     * Here, the invocation of {@code reachabilityFence} is nonintuitively
+     * placed <em>after</em> the call to {@code update}, to ensure that the
+     * array slot is not nulled out by {@link Object#finalize} before the
+     * update, even if the call to {@code action} was the last use of this
+     * object.  This might be the case if, for example a usage in a user program
+     * had the form {@code new Resource().action();} which retains no other
+     * reference to this {@code Resource}.  While probably overkill here,
+     * {@code reachabilityFence} is placed in a {@code finally} block to ensure
+     * that it is invoked across all paths in the method.  In a method with more
+     * complex control paths, you might need further precautions to ensure that
+     * {@code reachabilityFence} is encountered along all of them.
+     *
+     * <p> It is sometimes possible to better encapsulate use of
+     * {@code reachabilityFence}.  Continuing the above example, if it were
+     * acceptable for the call to method {@code update} to proceed even if the
+     * finalizer had already executed (nulling out slot), then you could
+     * localize use of {@code reachabilityFence}:
+     *
+     * <pre> {@code
+     * public void action2() {
+     *   // ...
+     *   Resource.update(getExternalResource());
+     * }
+     * private ExternalResource getExternalResource() {
+     *   ExternalResource ext = externalResourceArray[myIndex];
+     *   Reference.reachabilityFence(this);
+     *   return ext;
+     * }}</pre>
+     *
+     * <p> Method {@code reachabilityFence} is not required in constructions
+     * that themselves ensure reachability.  For example, because objects that
+     * are locked cannot, in general, be reclaimed, it would suffice if all
+     * accesses of the object, in all methods of class {@code Resource}
+     * (including {@code finalize}) were enclosed in {@code synchronized (this)}
+     * blocks.  (Further, such blocks must not include infinite loops, or
+     * themselves be unreachable, which fall into the corner case exceptions to
+     * the "in general" disclaimer.)  However, method {@code reachabilityFence}
+     * remains a better option in cases where this approach is not as efficient,
+     * desirable, or possible; for example because it would encounter deadlock.
+     *
+     * @param ref the reference. If {@code null}, this method has no effect.
+     * @since 9
+     */
+    @DontInline
+    public static void reachabilityFence(Object ref) {
+        // Does nothing, because this method is annotated with @DontInline
+        // HotSpot needs to retain the ref and not GC it before a call to this
+        // method
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ref/ReachabilityFenceTest.java	Tue Oct 06 18:42:06 2015 +0200
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8133348
+ * @summary Tests if reachabilityFence is working
+ *
+ * @run main/othervm -Xint                   -Dpremature=false ReachabilityFenceTest
+ * @run main/othervm -XX:TieredStopAtLevel=1 -Dpremature=true  ReachabilityFenceTest
+ * @run main/othervm -XX:TieredStopAtLevel=2 -Dpremature=true  ReachabilityFenceTest
+ * @run main/othervm -XX:TieredStopAtLevel=3 -Dpremature=true  ReachabilityFenceTest
+ * @run main/othervm -XX:TieredStopAtLevel=4 -Dpremature=true  ReachabilityFenceTest
+ */
+
+import java.lang.ref.Reference;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ReachabilityFenceTest {
+
+    /*
+     * Implementation notes:
+     *
+     * This test has positive and negative parts.
+     *
+     * Negative test is "nonFenced", and it tests that absent of reachabilityFence, the object can
+     * be prematurely finalized -- this validates the test itself. Not every VM mode is expected to
+     * prematurely finalize the objects, and -Dpremature option communicates that to test. If a VM mode
+     * passes the negative test, then our understanding of what could happen is correct, and we can
+     * go forward.
+     *
+     * Positive test is "fenced", and it checks that given the reachabilityFence at the end of the block,
+     * the object cannot be finalized. There is no sense running a positive test when premature finalization
+     * is not expected. It is a job for negative test to verify that invariant.
+     *
+     * The test methods should be appropriately compiled, therefore we do several iterations.
+     */
+
+    // Enough to OSR and compile
+    static final int LOOP_ITERS = Integer.getInteger("LOOP_ITERS", 50000);
+
+    // Enough after which to start triggering GC and finalization
+    static final int WARMUP_LOOP_ITERS = LOOP_ITERS - Integer.getInteger("GC_ITERS", 100);
+
+    // Enough to switch from an OSR'ed method to compiled method
+    static final int MAIN_ITERS = 3;
+
+    static final boolean PREMATURE_FINALIZATION = Boolean.getBoolean("premature");
+
+    public static void main(String... args) {
+        // Negative test
+        boolean finalized = false;
+        for (int c = 0; !finalized && c < MAIN_ITERS; c++) {
+            finalized |= nonFenced();
+        }
+
+        if (PREMATURE_FINALIZATION && !finalized) {
+            throw new IllegalStateException("The object had never been finalized before timeout reached.");
+        }
+
+        if (!PREMATURE_FINALIZATION && finalized) {
+            throw new IllegalStateException("The object had been finalized without a fence, even though we don't expect it.");
+        }
+
+        if (!PREMATURE_FINALIZATION)
+            return;
+
+        // Positive test
+        finalized = false;
+        for (int c = 0; !finalized && c < MAIN_ITERS; c++) {
+            finalized |= fenced();
+        }
+
+        if (finalized) {
+            throw new IllegalStateException("The object had been prematurely finalized.");
+        }
+    }
+
+    public static boolean nonFenced() {
+        AtomicBoolean finalized = new AtomicBoolean();
+        MyFinalizeable o = new MyFinalizeable(finalized);
+
+        for (int i = 0; i < LOOP_ITERS; i++) {
+            if (finalized.get()) break;
+            if (i > WARMUP_LOOP_ITERS) {
+                System.gc();
+                System.runFinalization();
+            }
+        }
+
+        return finalized.get();
+    }
+
+    public static boolean fenced() {
+        AtomicBoolean finalized = new AtomicBoolean();
+        MyFinalizeable o = new MyFinalizeable(finalized);
+
+        for (int i = 0; i < LOOP_ITERS; i++) {
+            if (finalized.get()) break;
+            if (i > WARMUP_LOOP_ITERS) {
+                System.gc();
+                System.runFinalization();
+            }
+        }
+
+        Reference.reachabilityFence(o);
+
+        return finalized.get();
+    }
+
+    private static class MyFinalizeable {
+        private final AtomicBoolean finalized;
+
+        public MyFinalizeable(AtomicBoolean b) {
+            this.finalized = b;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            finalized.set(true);
+        }
+    }
+}
\ No newline at end of file