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