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 x = 48 new Object() { 49 @Override 50 protected void finalize() { 51 latch.countDown(); 52 } 53 }; 54 x = null; // Hint to the JIT that x is unreachable 55 GcFinalization.await(latch); 56 assertEquals(0, latch.getCount()); 57 } 58 testAwaitDone_Future()59 public void testAwaitDone_Future() { 60 final SettableFuture<@Nullable Void> future = SettableFuture.create(); 61 Object x = 62 new Object() { 63 @Override 64 protected void finalize() { 65 future.set(null); 66 } 67 }; 68 x = null; // Hint to the JIT that x is unreachable 69 GcFinalization.awaitDone(future); 70 assertTrue(future.isDone()); 71 assertFalse(future.isCancelled()); 72 } 73 testAwaitDone_Future_Cancel()74 public void testAwaitDone_Future_Cancel() { 75 final SettableFuture<@Nullable Void> future = SettableFuture.create(); 76 Object x = 77 new Object() { 78 @Override 79 protected void finalize() { 80 future.cancel(false); 81 } 82 }; 83 x = null; // Hint to the JIT that x is unreachable 84 GcFinalization.awaitDone(future); 85 assertTrue(future.isDone()); 86 assertTrue(future.isCancelled()); 87 } 88 testAwaitClear()89 public void testAwaitClear() { 90 final WeakReference<Object> ref = new WeakReference<>(new Object()); 91 GcFinalization.awaitClear(ref); 92 assertNull(ref.get()); 93 } 94 testAwaitDone_FinalizationPredicate()95 public void testAwaitDone_FinalizationPredicate() { 96 final WeakHashMap<Object, Object> map = new WeakHashMap<>(); 97 map.put(new Object(), Boolean.TRUE); 98 GcFinalization.awaitDone( 99 new FinalizationPredicate() { 100 @Override 101 public boolean isDone() { 102 return map.isEmpty(); 103 } 104 }); 105 assertTrue(map.isEmpty()); 106 } 107 108 // ---------------------------------------------------------------- 109 // Test that interrupts result in RuntimeException, not InterruptedException. 110 // Trickier than it looks, because runFinalization swallows interrupts. 111 // ---------------------------------------------------------------- 112 113 class Interruptenator extends Thread { 114 final AtomicBoolean shutdown; 115 Interruptenator(final Thread interruptee)116 Interruptenator(final Thread interruptee) { 117 this(interruptee, new AtomicBoolean(false)); 118 } 119 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown)120 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) { 121 super( 122 new Runnable() { 123 @Override 124 public void run() { 125 while (!shutdown.get()) { 126 interruptee.interrupt(); 127 Thread.yield(); 128 } 129 } 130 }); 131 this.shutdown = shutdown; 132 start(); 133 } 134 shutdown()135 void shutdown() { 136 shutdown.set(true); 137 while (this.isAlive()) { 138 Thread.yield(); 139 } 140 } 141 } 142 assertWrapsInterruptedException(RuntimeException e)143 void assertWrapsInterruptedException(RuntimeException e) { 144 assertThat(e).hasMessageThat().contains("Unexpected interrupt"); 145 assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class); 146 } 147 testAwait_CountDownLatch_Interrupted()148 public void testAwait_CountDownLatch_Interrupted() { 149 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 150 try { 151 final CountDownLatch latch = new CountDownLatch(1); 152 RuntimeException expected = 153 assertThrows(RuntimeException.class, () -> GcFinalization.await(latch)); 154 assertWrapsInterruptedException(expected); 155 } finally { 156 interruptenator.shutdown(); 157 Thread.interrupted(); 158 } 159 } 160 testAwaitDone_Future_Interrupted_Interrupted()161 public void testAwaitDone_Future_Interrupted_Interrupted() { 162 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 163 try { 164 final SettableFuture<@Nullable Void> future = SettableFuture.create(); 165 RuntimeException expected = 166 assertThrows(RuntimeException.class, () -> GcFinalization.awaitDone(future)); 167 assertWrapsInterruptedException(expected); 168 } finally { 169 interruptenator.shutdown(); 170 Thread.interrupted(); 171 } 172 } 173 testAwaitClear_Interrupted()174 public void testAwaitClear_Interrupted() { 175 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 176 try { 177 final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE); 178 RuntimeException expected = 179 assertThrows(RuntimeException.class, () -> GcFinalization.awaitClear(ref)); 180 assertWrapsInterruptedException(expected); 181 } finally { 182 interruptenator.shutdown(); 183 Thread.interrupted(); 184 } 185 } 186 testAwaitDone_FinalizationPredicate_Interrupted()187 public void testAwaitDone_FinalizationPredicate_Interrupted() { 188 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 189 try { 190 RuntimeException expected = 191 assertThrows( 192 RuntimeException.class, 193 () -> 194 GcFinalization.awaitDone( 195 new FinalizationPredicate() { 196 @Override 197 public boolean isDone() { 198 return false; 199 } 200 })); 201 assertWrapsInterruptedException(expected); 202 } finally { 203 interruptenator.shutdown(); 204 Thread.interrupted(); 205 } 206 } 207 208 /** 209 * awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as 210 * the more direct await* methods, but should be reliable enough in practice to avoid flakiness of 211 * this test. (And if it isn't, we'd like to know about it first!) 212 */ testAwaitFullGc()213 public void testAwaitFullGc() { 214 final CountDownLatch finalizerRan = new CountDownLatch(1); 215 final WeakReference<Object> ref = 216 new WeakReference<Object>( 217 new Object() { 218 @Override 219 protected void finalize() { 220 finalizerRan.countDown(); 221 } 222 }); 223 224 // Don't copy this into your own test! 225 // Use e.g. awaitClear or await(CountDownLatch) instead. 226 GcFinalization.awaitFullGc(); 227 228 // If this test turns out to be flaky, add a second call to awaitFullGc() 229 // GcFinalization.awaitFullGc(); 230 231 assertEquals(0, finalizerRan.getCount()); 232 assertNull(ref.get()); 233 } 234 } 235