HHH-16911 Ensure the PhantomReference doesn't get collected before having had a change to trigger

This commit is contained in:
Sanne Grinovero 2023-07-28 08:34:57 -04:00 committed by Sanne Grinovero
parent 9d118a5482
commit b758ddafa6
1 changed files with 27 additions and 2 deletions

View File

@ -21,7 +21,7 @@ import org.junit.Assert;
*
* @author Sanne Grinovero (C) 2023 Red Hat Inc.
*/
public class PhantomReferenceLeakDetector {
public final class PhantomReferenceLeakDetector {
/**
* A single second should be more than enough; this might need
@ -34,6 +34,7 @@ public class PhantomReferenceLeakDetector {
*/
private static final int MAX_TOTAL_WAIT_SECONDS = 180;
private static final int GC_ATTEMPTS = MAX_TOTAL_WAIT_SECONDS * 5;
private static PhantomReference hold;
/**
* Asserts that a certain operation won't be leaking
@ -77,11 +78,35 @@ public class PhantomReferenceLeakDetector {
T criticalReference = action.get();
final ReferenceQueue<T> referenceQueue = new ReferenceQueue<>();
final PhantomReference<T> reference = new PhantomReference<>( criticalReference, referenceQueue );
holdOnTo( reference );
//Ignore IDE's suggestion to remove the following line: we really need it!
// (it could be inlined above, but I prefer this style so that it serves as an example for
// future maintenance of how this works)
criticalReference = null;
return verifyCollection( referenceQueue, gcAttempts, totalWaitSeconds );
try {
return verifyCollection( referenceQueue, gcAttempts, totalWaitSeconds );
}
finally {
clearHeldReferences();
}
}
private static synchronized void clearHeldReferences() {
PhantomReferenceLeakDetector.hold = null;
}
/**
* Ensure we keep a reference to the PhantomReference until we're done
* with the test, otherwise it might get collected before having had
* a change to trigger; this seems to behave differently on different
* JVMs but is most likely just a timing issue.
* @param reference
*/
private static synchronized void holdOnTo(PhantomReference reference) {
if ( PhantomReferenceLeakDetector.hold != null ) {
throw new IllegalStateException( "Detected recursive use of PhantomReferenceLeakDetector, which is not supported, or possibly a leaked previous run" );
}
PhantomReferenceLeakDetector.hold = reference;
}
private static <T> boolean verifyCollection(final ReferenceQueue<T> referenceQueue, final int gcAttempts, final int totalWaitSeconds) {