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