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 21 import com.google.common.testing.GcFinalization.FinalizationPredicate; 22 import com.google.common.util.concurrent.SettableFuture; 23 import java.lang.ref.WeakReference; 24 import java.util.WeakHashMap; 25 import java.util.concurrent.CountDownLatch; 26 import java.util.concurrent.atomic.AtomicBoolean; 27 import junit.framework.TestCase; 28 29 /** 30 * Tests for {@link GcFinalization}. 31 * 32 * @author Martin Buchholz 33 * @author mike nonemacher 34 */ 35 36 public class GcFinalizationTest extends TestCase { 37 38 // ---------------------------------------------------------------- 39 // Ordinary tests of successful method execution 40 // ---------------------------------------------------------------- 41 testAwait_CountDownLatch()42 public void testAwait_CountDownLatch() { 43 final CountDownLatch latch = new CountDownLatch(1); 44 Object x = 45 new Object() { 46 @Override 47 protected void finalize() { 48 latch.countDown(); 49 } 50 }; 51 x = null; // Hint to the JIT that x is unreachable 52 GcFinalization.await(latch); 53 assertEquals(0, latch.getCount()); 54 } 55 testAwaitDone_Future()56 public void testAwaitDone_Future() { 57 final SettableFuture<Void> future = SettableFuture.create(); 58 Object x = 59 new Object() { 60 @Override 61 protected void finalize() { 62 future.set(null); 63 } 64 }; 65 x = null; // Hint to the JIT that x is unreachable 66 GcFinalization.awaitDone(future); 67 assertTrue(future.isDone()); 68 assertFalse(future.isCancelled()); 69 } 70 testAwaitDone_Future_Cancel()71 public void testAwaitDone_Future_Cancel() { 72 final SettableFuture<Void> future = SettableFuture.create(); 73 Object x = 74 new Object() { 75 @Override 76 protected void finalize() { 77 future.cancel(false); 78 } 79 }; 80 x = null; // Hint to the JIT that x is unreachable 81 GcFinalization.awaitDone(future); 82 assertTrue(future.isDone()); 83 assertTrue(future.isCancelled()); 84 } 85 testAwaitClear()86 public void testAwaitClear() { 87 final WeakReference<Object> ref = new WeakReference<>(new Object()); 88 GcFinalization.awaitClear(ref); 89 assertNull(ref.get()); 90 } 91 testAwaitDone_FinalizationPredicate()92 public void testAwaitDone_FinalizationPredicate() { 93 final WeakHashMap<Object, Object> map = new WeakHashMap<>(); 94 map.put(new Object(), Boolean.TRUE); 95 GcFinalization.awaitDone( 96 new FinalizationPredicate() { 97 @Override 98 public boolean isDone() { 99 return map.isEmpty(); 100 } 101 }); 102 assertTrue(map.isEmpty()); 103 } 104 105 // ---------------------------------------------------------------- 106 // Test that interrupts result in RuntimeException, not InterruptedException. 107 // Trickier than it looks, because runFinalization swallows interrupts. 108 // ---------------------------------------------------------------- 109 110 class Interruptenator extends Thread { 111 final AtomicBoolean shutdown; 112 Interruptenator(final Thread interruptee)113 Interruptenator(final Thread interruptee) { 114 this(interruptee, new AtomicBoolean(false)); 115 } 116 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown)117 Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) { 118 super( 119 new Runnable() { 120 @Override 121 public void run() { 122 while (!shutdown.get()) { 123 interruptee.interrupt(); 124 Thread.yield(); 125 } 126 } 127 }); 128 this.shutdown = shutdown; 129 start(); 130 } 131 shutdown()132 void shutdown() { 133 shutdown.set(true); 134 while (this.isAlive()) { 135 Thread.yield(); 136 } 137 } 138 } 139 assertWrapsInterruptedException(RuntimeException e)140 void assertWrapsInterruptedException(RuntimeException e) { 141 assertThat(e).hasMessageThat().contains("Unexpected interrupt"); 142 assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class); 143 } 144 testAwait_CountDownLatch_Interrupted()145 public void testAwait_CountDownLatch_Interrupted() { 146 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 147 try { 148 final CountDownLatch latch = new CountDownLatch(1); 149 try { 150 GcFinalization.await(latch); 151 fail("should throw"); 152 } catch (RuntimeException expected) { 153 assertWrapsInterruptedException(expected); 154 } 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<Void> future = SettableFuture.create(); 165 try { 166 GcFinalization.awaitDone(future); 167 fail("should throw"); 168 } catch (RuntimeException expected) { 169 assertWrapsInterruptedException(expected); 170 } 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 try { 182 GcFinalization.awaitClear(ref); 183 fail("should throw"); 184 } catch (RuntimeException expected) { 185 assertWrapsInterruptedException(expected); 186 } 187 } finally { 188 interruptenator.shutdown(); 189 Thread.interrupted(); 190 } 191 } 192 testAwaitDone_FinalizationPredicate_Interrupted()193 public void testAwaitDone_FinalizationPredicate_Interrupted() { 194 Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); 195 try { 196 try { 197 GcFinalization.awaitDone( 198 new FinalizationPredicate() { 199 @Override 200 public boolean isDone() { 201 return false; 202 } 203 }); 204 fail("should throw"); 205 } catch (RuntimeException expected) { 206 assertWrapsInterruptedException(expected); 207 } 208 } finally { 209 interruptenator.shutdown(); 210 Thread.interrupted(); 211 } 212 } 213 214 /** 215 * awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as 216 * the more direct await* methods, but should be reliable enough in practice to avoid flakiness of 217 * this test. (And if it isn't, we'd like to know about it first!) 218 */ testAwaitFullGc()219 public void testAwaitFullGc() { 220 final CountDownLatch finalizerRan = new CountDownLatch(1); 221 final WeakReference<Object> ref = 222 new WeakReference<Object>( 223 new Object() { 224 @Override 225 protected void finalize() { 226 finalizerRan.countDown(); 227 } 228 }); 229 230 // Don't copy this into your own test! 231 // Use e.g. awaitClear or await(CountDownLatch) instead. 232 GcFinalization.awaitFullGc(); 233 234 // If this test turns out to be flaky, add a second call to awaitFullGc() 235 // GcFinalization.awaitFullGc(); 236 237 assertEquals(0, finalizerRan.getCount()); 238 assertNull(ref.get()); 239 } 240 } 241