1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.testing; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.common.testing.GcFinalization.FinalizationPredicate; 23 import com.google.common.util.concurrent.SettableFuture; 24 import java.lang.ref.WeakReference; 25 import java.util.WeakHashMap; 26 import java.util.concurrent.CountDownLatch; 27 import java.util.concurrent.atomic.AtomicBoolean; 28 import junit.framework.TestCase; 29 import org.checkerframework.checker.nullness.qual.Nullable; 30 31 /** 32 * Tests for {@link GcFinalization}. 33 * 34 * @author Martin Buchholz 35 * @author mike nonemacher 36 */ 37 @AndroidIncompatible // depends on details of gc 38 39 public class GcFinalizationTest extends TestCase { 40 41 // ---------------------------------------------------------------- 42 // Ordinary tests of successful method execution 43 // ---------------------------------------------------------------- 44 testAwait_countDownLatch()45 public void testAwait_countDownLatch() { 46 final CountDownLatch latch = new CountDownLatch(1); 47 Object unused = 48 new Object() { 49 @SuppressWarnings({"removal", "Finalize"}) // b/260137033 50 @Override 51 protected void finalize() { 52 latch.countDown(); 53 } 54 }; 55 unused = null; // Hint to the JIT that unused is unreachable 56 GcFinalization.await(latch); 57 assertEquals(0, latch.getCount()); 58 } 59 testAwaitDone_future()60 public void testAwaitDone_future() { 61 final SettableFuture<@Nullable Void> future = SettableFuture.create(); 62 Object unused = 63 new Object() { 64 @SuppressWarnings({"removal", "Finalize"}) // b/260137033 65 @Override 66 protected void finalize() { 67 future.set(null); 68 } 69 }; 70 unused = null; // Hint to the JIT that unused is unreachable 71 GcFinalization.awaitDone(future); 72 assertTrue(future.isDone()); 73 assertFalse(future.isCancelled()); 74 } 75 testAwaitDone_future_cancel()76 public void testAwaitDone_future_cancel() { 77 final SettableFuture<@Nullable Void> future = SettableFuture.create(); 78 Object unused = 79 new Object() { 80 @SuppressWarnings({"removal", "Finalize"}) // b/260137033 81 @Override 82 protected void finalize() { 83 future.cancel(false); 84 } 85 }; 86 unused = null; // Hint to the JIT that unused is unreachable 87 GcFinalization.awaitDone(future); 88 assertTrue(future.isDone()); 89 assertTrue(future.isCancelled()); 90 } 91 testAwaitClear()92 public void testAwaitClear() { 93 final WeakReference<Object> ref = new WeakReference<>(new Object()); 94 GcFinalization.awaitClear(ref); 95 assertNull(ref.get()); 96 } 97 testAwaitDone_finalizationPredicate()98 public void testAwaitDone_finalizationPredicate() { 99 final WeakHashMap<Object, Object> map = new WeakHashMap<>(); 100 map.put(new Object(), Boolean.TRUE); 101 GcFinalization.awaitDone( 102 new FinalizationPredicate() { 103 @Override 104 public boolean isDone() { 105 return map.isEmpty(); 106 } 107 }); 108 assertTrue(map.isEmpty()); 109 } 110 111 // ---------------------------------------------------------------- 112 // Test that interrupts result in RuntimeException, not InterruptedException. 113 // Trickier than it looks, because runFinalization swallows interrupts. 114 // ---------------------------------------------------------------- 115 116 class Interruptenator extends Thread { 117 final AtomicBoolean shutdown; 118 Interruptenator(final Thread interruptee)119 Interruptenator(final Thread interruptee) { 120 this(interruptee, new AtomicBoolean(false)); 121 } 122 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown)123 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) { 124 super( 125 new Runnable() { 126 @Override 127 public void run() { 128 while (!shutdown.get()) { 129 interruptee.interrupt(); 130 Thread.yield(); 131 } 132 } 133 }); 134 this.shutdown = shutdown; 135 start(); 136 } 137 shutdown()138 void shutdown() { 139 shutdown.set(true); 140 while (this.isAlive()) { 141 Thread.yield(); 142 } 143 } 144 } 145 assertWrapsInterruptedException(RuntimeException e)146 void assertWrapsInterruptedException(RuntimeException e) { 147 assertThat(e).hasMessageThat().contains("Unexpected interrupt"); 148 assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class); 149 } 150 testAwait_countDownLatch_interrupted()151 public void testAwait_countDownLatch_interrupted() { 152 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 153 try { 154 final CountDownLatch latch = new CountDownLatch(1); 155 RuntimeException expected = 156 assertThrows(RuntimeException.class, () -> GcFinalization.await(latch)); 157 assertWrapsInterruptedException(expected); 158 } finally { 159 interruptenator.shutdown(); 160 Thread.interrupted(); 161 } 162 } 163 testAwaitDone_future_interrupted_interrupted()164 public void testAwaitDone_future_interrupted_interrupted() { 165 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 166 try { 167 final SettableFuture<@Nullable Void> future = SettableFuture.create(); 168 RuntimeException expected = 169 assertThrows(RuntimeException.class, () -> GcFinalization.awaitDone(future)); 170 assertWrapsInterruptedException(expected); 171 } finally { 172 interruptenator.shutdown(); 173 Thread.interrupted(); 174 } 175 } 176 testAwaitClear_interrupted()177 public void testAwaitClear_interrupted() { 178 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 179 try { 180 final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE); 181 RuntimeException expected = 182 assertThrows(RuntimeException.class, () -> GcFinalization.awaitClear(ref)); 183 assertWrapsInterruptedException(expected); 184 } finally { 185 interruptenator.shutdown(); 186 Thread.interrupted(); 187 } 188 } 189 testAwaitDone_finalizationPredicate_interrupted()190 public void testAwaitDone_finalizationPredicate_interrupted() { 191 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 192 try { 193 RuntimeException expected = 194 assertThrows( 195 RuntimeException.class, 196 () -> 197 GcFinalization.awaitDone( 198 new FinalizationPredicate() { 199 @Override 200 public boolean isDone() { 201 return false; 202 } 203 })); 204 assertWrapsInterruptedException(expected); 205 } finally { 206 interruptenator.shutdown(); 207 Thread.interrupted(); 208 } 209 } 210 211 /** 212 * awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as 213 * the more direct await* methods, but should be reliable enough in practice to avoid flakiness of 214 * this test. (And if it isn't, we'd like to know about it first!) 215 */ testAwaitFullGc()216 public void testAwaitFullGc() { 217 final CountDownLatch finalizerRan = new CountDownLatch(1); 218 final WeakReference<Object> ref = 219 new WeakReference<Object>( 220 new Object() { 221 @SuppressWarnings({"removal", "Finalize"}) // b/260137033 222 @Override 223 protected void finalize() { 224 finalizerRan.countDown(); 225 } 226 }); 227 228 // Don't copy this into your own test! 229 // Use e.g. awaitClear or await(CountDownLatch) instead. 230 GcFinalization.awaitFullGc(); 231 232 // If this test turns out to be flaky, add a second call to awaitFullGc() 233 // GcFinalization.awaitFullGc(); 234 235 assertEquals(0, finalizerRan.getCount()); 236 assertNull(ref.get()); 237 } 238 } 239