• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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