241 lines
7.2 KiB
Java
241 lines
7.2 KiB
Java
/*
|
|
* Copyright (C) 2011 The Guava Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.common.testing;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
|
|
import com.google.common.testing.GcFinalization.FinalizationPredicate;
|
|
import com.google.common.util.concurrent.SettableFuture;
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.WeakHashMap;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import junit.framework.TestCase;
|
|
|
|
/**
|
|
* Tests for {@link GcFinalization}.
|
|
*
|
|
* @author Martin Buchholz
|
|
* @author mike nonemacher
|
|
*/
|
|
|
|
public class GcFinalizationTest extends TestCase {
|
|
|
|
// ----------------------------------------------------------------
|
|
// Ordinary tests of successful method execution
|
|
// ----------------------------------------------------------------
|
|
|
|
public void testAwait_CountDownLatch() {
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
Object x =
|
|
new Object() {
|
|
@Override
|
|
protected void finalize() {
|
|
latch.countDown();
|
|
}
|
|
};
|
|
x = null; // Hint to the JIT that x is unreachable
|
|
GcFinalization.await(latch);
|
|
assertEquals(0, latch.getCount());
|
|
}
|
|
|
|
public void testAwaitDone_Future() {
|
|
final SettableFuture<Void> future = SettableFuture.create();
|
|
Object x =
|
|
new Object() {
|
|
@Override
|
|
protected void finalize() {
|
|
future.set(null);
|
|
}
|
|
};
|
|
x = null; // Hint to the JIT that x is unreachable
|
|
GcFinalization.awaitDone(future);
|
|
assertTrue(future.isDone());
|
|
assertFalse(future.isCancelled());
|
|
}
|
|
|
|
public void testAwaitDone_Future_Cancel() {
|
|
final SettableFuture<Void> future = SettableFuture.create();
|
|
Object x =
|
|
new Object() {
|
|
@Override
|
|
protected void finalize() {
|
|
future.cancel(false);
|
|
}
|
|
};
|
|
x = null; // Hint to the JIT that x is unreachable
|
|
GcFinalization.awaitDone(future);
|
|
assertTrue(future.isDone());
|
|
assertTrue(future.isCancelled());
|
|
}
|
|
|
|
public void testAwaitClear() {
|
|
final WeakReference<Object> ref = new WeakReference<>(new Object());
|
|
GcFinalization.awaitClear(ref);
|
|
assertNull(ref.get());
|
|
}
|
|
|
|
public void testAwaitDone_FinalizationPredicate() {
|
|
final WeakHashMap<Object, Object> map = new WeakHashMap<>();
|
|
map.put(new Object(), Boolean.TRUE);
|
|
GcFinalization.awaitDone(
|
|
new FinalizationPredicate() {
|
|
@Override
|
|
public boolean isDone() {
|
|
return map.isEmpty();
|
|
}
|
|
});
|
|
assertTrue(map.isEmpty());
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
// Test that interrupts result in RuntimeException, not InterruptedException.
|
|
// Trickier than it looks, because runFinalization swallows interrupts.
|
|
// ----------------------------------------------------------------
|
|
|
|
class Interruptenator extends Thread {
|
|
final AtomicBoolean shutdown;
|
|
|
|
Interruptenator(final Thread interruptee) {
|
|
this(interruptee, new AtomicBoolean(false));
|
|
}
|
|
|
|
Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) {
|
|
super(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
while (!shutdown.get()) {
|
|
interruptee.interrupt();
|
|
Thread.yield();
|
|
}
|
|
}
|
|
});
|
|
this.shutdown = shutdown;
|
|
start();
|
|
}
|
|
|
|
void shutdown() {
|
|
shutdown.set(true);
|
|
while (this.isAlive()) {
|
|
Thread.yield();
|
|
}
|
|
}
|
|
}
|
|
|
|
void assertWrapsInterruptedException(RuntimeException e) {
|
|
assertThat(e).hasMessageThat().contains("Unexpected interrupt");
|
|
assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class);
|
|
}
|
|
|
|
public void testAwait_CountDownLatch_Interrupted() {
|
|
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
|
|
try {
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
try {
|
|
GcFinalization.await(latch);
|
|
fail("should throw");
|
|
} catch (RuntimeException expected) {
|
|
assertWrapsInterruptedException(expected);
|
|
}
|
|
} finally {
|
|
interruptenator.shutdown();
|
|
Thread.interrupted();
|
|
}
|
|
}
|
|
|
|
public void testAwaitDone_Future_Interrupted_Interrupted() {
|
|
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
|
|
try {
|
|
final SettableFuture<Void> future = SettableFuture.create();
|
|
try {
|
|
GcFinalization.awaitDone(future);
|
|
fail("should throw");
|
|
} catch (RuntimeException expected) {
|
|
assertWrapsInterruptedException(expected);
|
|
}
|
|
} finally {
|
|
interruptenator.shutdown();
|
|
Thread.interrupted();
|
|
}
|
|
}
|
|
|
|
public void testAwaitClear_Interrupted() {
|
|
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
|
|
try {
|
|
final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE);
|
|
try {
|
|
GcFinalization.awaitClear(ref);
|
|
fail("should throw");
|
|
} catch (RuntimeException expected) {
|
|
assertWrapsInterruptedException(expected);
|
|
}
|
|
} finally {
|
|
interruptenator.shutdown();
|
|
Thread.interrupted();
|
|
}
|
|
}
|
|
|
|
public void testAwaitDone_FinalizationPredicate_Interrupted() {
|
|
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
|
|
try {
|
|
try {
|
|
GcFinalization.awaitDone(
|
|
new FinalizationPredicate() {
|
|
@Override
|
|
public boolean isDone() {
|
|
return false;
|
|
}
|
|
});
|
|
fail("should throw");
|
|
} catch (RuntimeException expected) {
|
|
assertWrapsInterruptedException(expected);
|
|
}
|
|
} finally {
|
|
interruptenator.shutdown();
|
|
Thread.interrupted();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as
|
|
* the more direct await* methods, but should be reliable enough in practice to avoid flakiness of
|
|
* this test. (And if it isn't, we'd like to know about it first!)
|
|
*/
|
|
public void testAwaitFullGc() {
|
|
final CountDownLatch finalizerRan = new CountDownLatch(1);
|
|
final WeakReference<Object> ref =
|
|
new WeakReference<Object>(
|
|
new Object() {
|
|
@Override
|
|
protected void finalize() {
|
|
finalizerRan.countDown();
|
|
}
|
|
});
|
|
|
|
// Don't copy this into your own test!
|
|
// Use e.g. awaitClear or await(CountDownLatch) instead.
|
|
GcFinalization.awaitFullGc();
|
|
|
|
// If this test turns out to be flaky, add a second call to awaitFullGc()
|
|
// GcFinalization.awaitFullGc();
|
|
|
|
assertEquals(0, finalizerRan.getCount());
|
|
assertNull(ref.get());
|
|
}
|
|
}
|