• 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           public boolean isDone() {
98             return map.isEmpty();
99           }
100         });
101     assertTrue(map.isEmpty());
102   }
103 
104   // ----------------------------------------------------------------
105   // Test that interrupts result in RuntimeException, not InterruptedException.
106   // Trickier than it looks, because runFinalization swallows interrupts.
107   // ----------------------------------------------------------------
108 
109   class Interruptenator extends Thread {
110     final AtomicBoolean shutdown;
111 
Interruptenator(final Thread interruptee)112     Interruptenator(final Thread interruptee) {
113       this(interruptee, new AtomicBoolean(false));
114     }
115 
Interruptenator(final Thread interruptee, final AtomicBoolean shutdown)116     Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) {
117       super(
118           new Runnable() {
119             public void run() {
120               while (!shutdown.get()) {
121                 interruptee.interrupt();
122                 Thread.yield();
123               }
124             }
125           });
126       this.shutdown = shutdown;
127       start();
128     }
129 
shutdown()130     void shutdown() {
131       shutdown.set(true);
132       while (this.isAlive()) {
133         Thread.yield();
134       }
135     }
136   }
137 
assertWrapsInterruptedException(RuntimeException e)138   void assertWrapsInterruptedException(RuntimeException e) {
139     assertThat(e).hasMessageThat().contains("Unexpected interrupt");
140     assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class);
141   }
142 
testAwait_CountDownLatch_Interrupted()143   public void testAwait_CountDownLatch_Interrupted() {
144     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
145     try {
146       final CountDownLatch latch = new CountDownLatch(1);
147       try {
148         GcFinalization.await(latch);
149         fail("should throw");
150       } catch (RuntimeException expected) {
151         assertWrapsInterruptedException(expected);
152       }
153     } finally {
154       interruptenator.shutdown();
155       Thread.interrupted();
156     }
157   }
158 
testAwaitDone_Future_Interrupted_Interrupted()159   public void testAwaitDone_Future_Interrupted_Interrupted() {
160     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
161     try {
162       final SettableFuture<Void> future = SettableFuture.create();
163       try {
164         GcFinalization.awaitDone(future);
165         fail("should throw");
166       } catch (RuntimeException expected) {
167         assertWrapsInterruptedException(expected);
168       }
169     } finally {
170       interruptenator.shutdown();
171       Thread.interrupted();
172     }
173   }
174 
testAwaitClear_Interrupted()175   public void testAwaitClear_Interrupted() {
176     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
177     try {
178       final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE);
179       try {
180         GcFinalization.awaitClear(ref);
181         fail("should throw");
182       } catch (RuntimeException expected) {
183         assertWrapsInterruptedException(expected);
184       }
185     } finally {
186       interruptenator.shutdown();
187       Thread.interrupted();
188     }
189   }
190 
testAwaitDone_FinalizationPredicate_Interrupted()191   public void testAwaitDone_FinalizationPredicate_Interrupted() {
192     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
193     try {
194       try {
195         GcFinalization.awaitDone(
196             new FinalizationPredicate() {
197               public boolean isDone() {
198                 return false;
199               }
200             });
201         fail("should throw");
202       } catch (RuntimeException expected) {
203         assertWrapsInterruptedException(expected);
204       }
205     } finally {
206       interruptenator.shutdown();
207       Thread.interrupted();
208     }
209   }
210 
211   /**
212    * awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as
213    * the more direct await* methods, but should be reliable enough in practice to avoid flakiness of
214    * this test. (And if it isn't, we'd like to know about it first!)
215    */
testAwaitFullGc()216   public void testAwaitFullGc() {
217     final CountDownLatch finalizerRan = new CountDownLatch(1);
218     final WeakReference<Object> ref =
219         new WeakReference<Object>(
220             new Object() {
221               @Override
222               protected void finalize() {
223                 finalizerRan.countDown();
224               }
225             });
226 
227     // Don't copy this into your own test!
228     // Use e.g. awaitClear or await(CountDownLatch) instead.
229     GcFinalization.awaitFullGc();
230 
231     // If this test turns out to be flaky, add a second call to awaitFullGc()
232     // GcFinalization.awaitFullGc();
233 
234     assertEquals(0, finalizerRan.getCount());
235     assertNull(ref.get());
236   }
237 }
238