Skip to content

Commit

Permalink
Backport 82a8bd7e92a1867b0c82f051361938be8610428d
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeybakhtin committed Mar 29, 2024
1 parent 5d4de36 commit f8927a1
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 87 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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!");
}
}
Expand Down Expand Up @@ -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");
}
}
}
4 changes: 1 addition & 3 deletions test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java
Original file line number Diff line number Diff line change
Expand Up @@ -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..");
}
}
Expand Down
77 changes: 37 additions & 40 deletions test/lib/jdk/test/lib/util/ForceGC.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object> queue = new ReferenceQueue<>();
Object obj = new Object();
PhantomReference<Object> 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();
}
}

0 comments on commit f8927a1

Please sign in to comment.