• 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 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