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