diff --git a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java index 3a99760bb6e..1f6f48817ff 100644 --- a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java +++ b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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 @@ -61,8 +61,7 @@ public void testClassLoaderLeak() throws Exception { con = null; assertNotNull(myOwnClassLoaderWeakReference.get()); - ForceGC gc = new ForceGC(); - assertTrue(gc.await(() -> myOwnClassLoaderWeakReference.get() == null)); + assertTrue(ForceGC.wait(() -> myOwnClassLoaderWeakReference.get() == null)); } } diff --git a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java index 5ccffa2020d..59a6679ad5a 100644 --- a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java +++ b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, 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 @@ -34,7 +34,6 @@ */ import java.io.IOException; -import java.lang.ref.Cleaner; import java.lang.ref.WeakReference; import java.lang.reflect.*; import java.net.MalformedURLException; @@ -43,11 +42,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.BooleanSupplier; import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.util.ForceGC; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -110,9 +107,7 @@ private void load(String classname) throws Exception { WeakReference weakLoader = loadAndRunClass(classname); // Force garbage collection to trigger unloading of class loader - new ForceGC().await(() -> weakLoader.get() == null); - - if (weakLoader.get() != null) { + if (!ForceGC.wait(() -> weakLoader.get() == null)) { throw new RuntimeException("Class " + classname + " not unloaded!"); } } @@ -141,37 +136,4 @@ static URL[] toURLs() { super("testloader", toURLs(), ClassLoader.getSystemClassLoader()); } } - - /** - * Utility class to invoke System.gc() - */ - static class ForceGC { - private final CountDownLatch cleanerInvoked = new CountDownLatch(1); - private final Cleaner cleaner = Cleaner.create(); - - ForceGC() { - cleaner.register(new Object(), () -> cleanerInvoked.countDown()); - } - - void doit() { - try { - for (int i = 0; i < 10; i++) { - System.gc(); - if (cleanerInvoked.await(1L, TimeUnit.SECONDS)) { - return; - } - } - } catch (InterruptedException unexpected) { - throw new AssertionError("unexpected InterruptedException"); - } - } - - void await(BooleanSupplier s) { - for (int i = 0; i < 10; i++) { - if (s.getAsBoolean()) return; - doit(); - } - throw new AssertionError("failed to satisfy condition"); - } - } } diff --git a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java index 353a5b99a64..c1dca807a60 100644 --- a/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java +++ b/test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java @@ -90,10 +90,8 @@ public static void main(String[] args) throws Exception { Security.removeProvider(providers[i].getName()); providers[i] = null; - ForceGC gc = new ForceGC(); int finalI = i; - gc.await(() -> weakRef[finalI].get() == null); - if (weakRef[i].get() != null) { + if (!ForceGC.wait(() -> weakRef[finalI].get() == null)) { throw new RuntimeException("Expected SunPKCS11 Provider to be GC'ed.."); } } diff --git a/test/lib/jdk/test/lib/util/ForceGC.java b/test/lib/jdk/test/lib/util/ForceGC.java index 888869dc53a..d2357028b36 100644 --- a/test/lib/jdk/test/lib/util/ForceGC.java +++ b/test/lib/jdk/test/lib/util/ForceGC.java @@ -23,57 +23,54 @@ package jdk.test.lib.util; -import java.lang.ref.Cleaner; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; import java.util.function.BooleanSupplier; /** * Utility class to invoke System.gc() */ public class ForceGC { - private final CountDownLatch cleanerInvoked = new CountDownLatch(1); - private final Cleaner cleaner = Cleaner.create(); - private Object o; - - public ForceGC() { - this.o = new Object(); - cleaner.register(o, () -> cleanerInvoked.countDown()); - } - - private void doit(int iter) { - try { - for (int i = 0; i < 10; i++) { - System.gc(); - System.out.println("doit() iter: " + iter + ", gc " + i); - if (cleanerInvoked.await(1L, TimeUnit.SECONDS)) { - return; - } - } - } catch (InterruptedException unexpected) { - throw new AssertionError("unexpected InterruptedException"); - } - } + // The jtreg testing timeout factor. + private static final double TIMEOUT_FACTOR = Double.valueOf( + System.getProperty("test.timeout.factor", "1.0")); /** - * Causes the current thread to wait until the {@code BooleanSupplier} returns true, - * unless the thread is interrupted or a predefined waiting time elapses. + * Causes the current thread to wait until the {@code booleanSupplier} + * returns true, or a specific waiting time elapses. The waiting time + * is 1 second scaled with the jtreg testing timeout factor. * - * @param s boolean supplier - * @return true if the {@code BooleanSupplier} returns true and false if - * the predefined waiting time elapsed before the count reaches zero. - * @throws InterruptedException if the current thread is interrupted while waiting + * @param booleanSupplier boolean supplier + * @return true if the {@code booleanSupplier} returns true, or false + * if did not complete after the specific waiting time. */ - public boolean await(BooleanSupplier s) { - o = null; // Keep reference to Object until now, to ensure the Cleaner - // doesn't count down the latch before await() is called. - for (int i = 0; i < 10; i++) { - if (s.getAsBoolean()) return true; - doit(i); - try { Thread.sleep(1000); } catch (InterruptedException e) { - throw new AssertionError("unexpected interrupted sleep", e); + public static boolean wait(BooleanSupplier booleanSupplier) { + ReferenceQueue queue = new ReferenceQueue<>(); + Object obj = new Object(); + PhantomReference ref = new PhantomReference<>(obj, queue); + obj = null; + Reference.reachabilityFence(obj); + Reference.reachabilityFence(ref); + + int retries = (int)(Math.round(1000L * TIMEOUT_FACTOR) / 200); + for (; retries >= 0; retries--) { + if (booleanSupplier.getAsBoolean()) { + return true; + } + + System.gc(); + + try { + // The remove() will always block for the specified milliseconds + // if the reference has already been removed from the queue. + // But it is fine. For most cases, the 1st GC is sufficient + // to trigger and complete the cleanup. + queue.remove(200L); + } catch (InterruptedException ie) { + // ignore, the loop will try again } } - return false; + return booleanSupplier.getAsBoolean(); } }