--- old/make/test/JtregNative.gmk 2017-04-14 14:35:01.532282260 -0400 +++ new/make/test/JtregNative.gmk 2017-04-14 14:35:01.400275554 -0400 @@ -43,6 +43,7 @@ # Add more directories here when needed. BUILD_HOTSPOT_JTREG_NATIVE_SRC += \ + $(HOTSPOT_TOPDIR)/test/gc/g1/TestJNIWeakG1 \ $(HOTSPOT_TOPDIR)/test/native_sanity \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8025979 \ $(HOTSPOT_TOPDIR)/test/runtime/jni/8033445 \ --- /dev/null 2017-04-05 16:10:48.427197190 -0400 +++ new/test/gc/g1/TestJNIWeakG1/TestJNIWeakG1.java 2017-04-14 14:35:01.948303396 -0400 @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017, 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 8166188 8178813 + * @summary Test return of JNI weak global refs during concurrent + * marking, verifying the use of the G1 load barrier to keep the + * referent alive. + * @key gc + * @requires vm.gc.G1 + * @requires vm.opt.ExplicitGCInvokesConcurrent != true + * @modules java.base + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/native + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UseG1GC -XX:MaxTenuringThreshold=2 + * -Xint + * TestJNIWeakG1 + * @run main/othervm/native + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UseG1GC -XX:MaxTenuringThreshold=2 + * -Xcomp + * TestJNIWeakG1 + */ + +import sun.hotspot.WhiteBox; + +import java.lang.ref.Reference; + +public final class TestJNIWeakG1 { + + static { + System.loadLibrary("TestJNIWeakG1"); + } + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + private static final class TestObject { + public final int value; + + public TestObject(int value) { + this.value = value; + } + } + + private static volatile TestObject testObject = null; + + private static native void registerObject(Object o); + private static native void unregisterObject(); + private static native Object getReturnedWeak(); + private static native Object getResolvedWeak(); + + // resolve controls whether getObject returns an explicitly + // resolved jweak value, or returns the jweak directly and invokes + // the implicit resolution in the native call return value + // handling path. + private static boolean resolve = true; + private static Object getObject() { + if (resolve) { + return getResolvedWeak(); + } else { + return getReturnedWeak(); + } + } + + // Create the test object and record it both strongly and weakly. + private static void remember(int value) { + TestObject o = new TestObject(value); + registerObject(o); + testObject = o; + } + + // Remove both strong and weak references to the current test object. + private static void forget() { + unregisterObject(); + testObject = null; + } + + // Repeatedly call System.gc() until o is in the old generation. + private static void gcUntilOld(Object o) { + while (!WB.isObjectInOldGen(o)) { + System.gc(); + } + } + + // Verify the weakly recorded object + private static void checkValue(int value) throws Exception { + Object o = getObject(); + if (o == null) { + throw new RuntimeException("Weak reference unexpectedly null"); + } + TestObject t = (TestObject)o; + if (t.value != value) { + throw new RuntimeException("Incorrect value"); + } + } + + // Verify we can create a weak reference and get it back. + private static void checkSanity() throws Exception { + System.out.println("running checkSanity"); + try { + // Inhibit concurrent GC during this check. + WB.requestConcurrentGCPhase("IDLE"); + + int value = 5; + try { + remember(value); + checkValue(value); + } finally { + forget(); + } + + } finally { + // Remove request. + WB.requestConcurrentGCPhase("ANY"); + } + } + + // Verify weak ref value survives across collection if strong ref exists. + private static void checkSurvival() throws Exception { + System.out.println("running checkSurvival"); + try { + int value = 10; + try { + remember(value); + checkValue(value); + gcUntilOld(testObject); + // Run a concurrent collection after object is old. + WB.requestConcurrentGCPhase("CONCURRENT_CYCLE"); + WB.requestConcurrentGCPhase("IDLE"); + // Verify weak ref still has expected value. + checkValue(value); + } finally { + forget(); + } + } finally { + // Remove request. + WB.requestConcurrentGCPhase("ANY"); + } + } + + // Verify weak ref cleared if no strong ref exists. + private static void checkClear() throws Exception { + System.out.println("running checkClear"); + try { + int value = 15; + try { + remember(value); + checkValue(value); + gcUntilOld(testObject); + // Run a concurrent collection after object is old. + WB.requestConcurrentGCPhase("CONCURRENT_CYCLE"); + WB.requestConcurrentGCPhase("IDLE"); + checkValue(value); + testObject = null; + // Run a concurrent collection after strong ref removed. + WB.requestConcurrentGCPhase("CONCURRENT_CYCLE"); + WB.requestConcurrentGCPhase("IDLE"); + // Verify weak ref cleared as expected. + Object recorded = getObject(); + if (recorded != null) { + throw new RuntimeException("expected clear"); + } + } finally { + forget(); + } + } finally { + // Remove request. + WB.requestConcurrentGCPhase("ANY"); + } + } + + // Verify weak ref not cleared if no strong ref at start of + // collection but weak ref read during marking. + private static void checkShouldNotClear() throws Exception { + System.out.println("running checkShouldNotCrash"); + try { + int value = 20; + try { + remember(value); + checkValue(value); + gcUntilOld(testObject); + // Block concurrent cycle until we're ready. + WB.requestConcurrentGCPhase("IDLE"); + checkValue(value); + testObject = null; // Discard strong ref + // Run through mark_from_roots. + WB.requestConcurrentGCPhase("BEFORE_REMARK"); + // Fetch weak ref'ed object. Should be kept alive now. + Object recovered = getObject(); + if (recovered == null) { + throw new RuntimeException("unexpected clear during mark"); + } + // Finish collection, including reference processing. + // Block any further cycles while we finish check. + WB.requestConcurrentGCPhase("IDLE"); + // Fetch weak ref'ed object. Referent is manifestly + // live in recovered; the earlier fetch should have + // kept it alive through collection, so weak ref + // should not have been cleared. + if (getObject() == null) { + // 8166188 problem results in not doing the + // keep-alive of earlier getObject result, so + // recovered is now reachable but not marked. + // We may eventually crash. + throw new RuntimeException("cleared jweak for live object"); + } + Reference.reachabilityFence(recovered); + } finally { + forget(); + } + } finally { + // Remove request. + WB.requestConcurrentGCPhase("ANY"); + } + } + + private static boolean check(boolean requestResolved) { + String kind = requestResolved ? "resolved" : "returned"; + System.out.println("Check with jweak " + kind); + resolve = requestResolved; + try { + checkSanity(); + checkSurvival(); + checkClear(); + checkShouldNotClear(); + System.out.println("Check passed"); + return true; + } catch (Exception e) { + System.out.println("Check failed: " + e.toString()); + return false; + } + } + + public static void main(String[] args) throws Exception { + // Perform check with direct jweak resolution. + boolean resolvedChecked = check(true); + // Perform check with implicit jweak resolution by native + // call's return value handling. + boolean returnedChecked = check(false); + if (resolvedChecked && returnedChecked) { + System.out.println("Test passed"); + } else { + throw new RuntimeException("Test failed"); + } + } +} --- /dev/null 2017-04-05 16:10:48.427197190 -0400 +++ new/test/gc/g1/TestJNIWeakG1/libTestJNIWeakG1.c 2017-04-14 14:35:02.440328391 -0400 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 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. + */ + +/* + * Native support for TestJNIWeakG1 test. + */ + +#include "jni.h" + +static jweak registered = NULL; + +JNIEXPORT void JNICALL +Java_TestJNIWeakG1_registerObject(JNIEnv* env, jclass jclazz, jobject value) { + // assert registered == NULL + registered = (*env)->NewWeakGlobalRef(env, value); +} + +JNIEXPORT void JNICALL +Java_TestJNIWeakG1_unregisterObject(JNIEnv* env, jclass jclazz) { + if (registered != NULL) { + (*env)->DeleteWeakGlobalRef(env, registered); + registered = NULL; + } +} + +// Directly return jweak, to be resolved by native call's return value handling. +JNIEXPORT jobject JNICALL +Java_TestJNIWeakG1_getReturnedWeak(JNIEnv* env, jclass jclazz) { + // assert registered != NULL + return registered; +} + +// Directly resolve jweak and return the result. +JNIEXPORT jobject JNICALL +Java_TestJNIWeakG1_getResolvedWeak(JNIEnv* env, jclass jclazz) { + // assert registered != NULL + return (*env)->NewLocalRef(env, registered); +}