• 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 unused =
48         new Object() {
49           @SuppressWarnings({"removal", "Finalize"}) // b/260137033
50           @Override
51           protected void finalize() {
52             latch.countDown();
53           }
54         };
55     unused = null; // Hint to the JIT that unused is unreachable
56     GcFinalization.await(latch);
57     assertEquals(0, latch.getCount());
58   }
59 
testAwaitDone_future()60   public void testAwaitDone_future() {
61     final SettableFuture<@Nullable Void> future = SettableFuture.create();
62     Object unused =
63         new Object() {
64           @SuppressWarnings({"removal", "Finalize"}) // b/260137033
65           @Override
66           protected void finalize() {
67             future.set(null);
68           }
69         };
70     unused = null; // Hint to the JIT that unused is unreachable
71     GcFinalization.awaitDone(future);
72     assertTrue(future.isDone());
73     assertFalse(future.isCancelled());
74   }
75 
testAwaitDone_future_cancel()76   public void testAwaitDone_future_cancel() {
77     final SettableFuture<@Nullable Void> future = SettableFuture.create();
78     Object unused =
79         new Object() {
80           @SuppressWarnings({"removal", "Finalize"}) // b/260137033
81           @Override
82           protected void finalize() {
83             future.cancel(false);
84           }
85         };
86     unused = null; // Hint to the JIT that unused is unreachable
87     GcFinalization.awaitDone(future);
88     assertTrue(future.isDone());
89     assertTrue(future.isCancelled());
90   }
91 
testAwaitClear()92   public void testAwaitClear() {
93     final WeakReference<Object> ref = new WeakReference<>(new Object());
94     GcFinalization.awaitClear(ref);
95     assertNull(ref.get());
96   }
97 
testAwaitDone_finalizationPredicate()98   public void testAwaitDone_finalizationPredicate() {
99     final WeakHashMap<Object, Object> map = new WeakHashMap<>();
100     map.put(new Object(), Boolean.TRUE);
101     GcFinalization.awaitDone(
102         new FinalizationPredicate() {
103           @Override
104           public boolean isDone() {
105             return map.isEmpty();
106           }
107         });
108     assertTrue(map.isEmpty());
109   }
110 
111   // ----------------------------------------------------------------
112   // Test that interrupts result in RuntimeException, not InterruptedException.
113   // Trickier than it looks, because runFinalization swallows interrupts.
114   // ----------------------------------------------------------------
115 
116   class Interruptenator extends Thread {
117     final AtomicBoolean shutdown;
118 
Interruptenator(final Thread interruptee)119     Interruptenator(final Thread interruptee) {
120       this(interruptee, new AtomicBoolean(false));
121     }
122 
Interruptenator(final Thread interruptee, final AtomicBoolean shutdown)123     Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) {
124       super(
125           new Runnable() {
126             @Override
127             public void run() {
128               while (!shutdown.get()) {
129                 interruptee.interrupt();
130                 Thread.yield();
131               }
132             }
133           });
134       this.shutdown = shutdown;
135       start();
136     }
137 
shutdown()138     void shutdown() {
139       shutdown.set(true);
140       while (this.isAlive()) {
141         Thread.yield();
142       }
143     }
144   }
145 
assertWrapsInterruptedException(RuntimeException e)146   void assertWrapsInterruptedException(RuntimeException e) {
147     assertThat(e).hasMessageThat().contains("Unexpected interrupt");
148     assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class);
149   }
150 
testAwait_countDownLatch_interrupted()151   public void testAwait_countDownLatch_interrupted() {
152     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
153     try {
154       final CountDownLatch latch = new CountDownLatch(1);
155       RuntimeException expected =
156           assertThrows(RuntimeException.class, () -> GcFinalization.await(latch));
157       assertWrapsInterruptedException(expected);
158     } finally {
159       interruptenator.shutdown();
160       Thread.interrupted();
161     }
162   }
163 
testAwaitDone_future_interrupted_interrupted()164   public void testAwaitDone_future_interrupted_interrupted() {
165     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
166     try {
167       final SettableFuture<@Nullable Void> future = SettableFuture.create();
168       RuntimeException expected =
169           assertThrows(RuntimeException.class, () -> GcFinalization.awaitDone(future));
170       assertWrapsInterruptedException(expected);
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       RuntimeException expected =
182           assertThrows(RuntimeException.class, () -> GcFinalization.awaitClear(ref));
183       assertWrapsInterruptedException(expected);
184     } finally {
185       interruptenator.shutdown();
186       Thread.interrupted();
187     }
188   }
189 
testAwaitDone_finalizationPredicate_interrupted()190   public void testAwaitDone_finalizationPredicate_interrupted() {
191     Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
192     try {
193       RuntimeException expected =
194           assertThrows(
195               RuntimeException.class,
196               () ->
197                   GcFinalization.awaitDone(
198                       new FinalizationPredicate() {
199                         @Override
200                         public boolean isDone() {
201                           return false;
202                         }
203                       }));
204       assertWrapsInterruptedException(expected);
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               @SuppressWarnings({"removal", "Finalize"}) // b/260137033
222               @Override
223               protected void finalize() {
224                 finalizerRan.countDown();
225               }
226             });
227 
228     // Don't copy this into your own test!
229     // Use e.g. awaitClear or await(CountDownLatch) instead.
230     GcFinalization.awaitFullGc();
231 
232     // If this test turns out to be flaky, add a second call to awaitFullGc()
233     // GcFinalization.awaitFullGc();
234 
235     assertEquals(0, finalizerRan.getCount());
236     assertNull(ref.get());
237   }
238 }
239