View Javadoc
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  
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  
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  
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  
74    public void testAwaitClear() {
75      final WeakReference<Object> ref = new WeakReference<Object>(new Object());
76      GcFinalization.awaitClear(ref);
77      assertNull(ref.get());
78    }
79  
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;
98      Interruptenator(final Thread interruptee) {
99        this(interruptee, new AtomicBoolean(false));
100     }
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     }
112     void shutdown() {
113       shutdown.set(true);
114       while (this.isAlive()) {
115         Thread.yield();
116       }
117     }
118   }
119 
120   void assertWrapsInterruptedException(RuntimeException e) {
121     assertTrue(e.getMessage().contains("Unexpected interrupt"));
122     assertTrue(e.getCause() instanceof InterruptedException);
123   }
124 
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 
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 
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 
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    */
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 }