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