1/*
2 * Copyright (C) 2008 Google Inc.
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.base.internal;
18
19import java.lang.ref.PhantomReference;
20import java.lang.ref.Reference;
21import java.lang.ref.ReferenceQueue;
22import java.lang.ref.WeakReference;
23import java.lang.reflect.Field;
24import java.lang.reflect.Method;
25import java.util.logging.Level;
26import java.util.logging.Logger;
27
28/**
29 * Thread that finalizes referents. All references should implement
30 * {@code com.google.common.base.FinalizableReference}.
31 *
32 * <p>While this class is public, we consider it to be *internal* and not part
33 * of our published API. It is public so we can access it reflectively across
34 * class loaders in secure environments.
35 *
36 * <p>This class can't depend on other Google Collections code. If we were
37 * to load this class in the same class loader as the rest of
38 * Google Collections, this thread would keep an indirect strong reference
39 * to the class loader and prevent it from being garbage collected. This
40 * poses a problem for environments where you want to throw away the class
41 * loader. For example, dynamically reloading a web application or unloading
42 * an OSGi bundle.
43 *
44 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
45 * in its own class loader. That way, this class doesn't prevent the main
46 * class loader from getting garbage collected, and this class can detect when
47 * the main class loader has been garbage collected and stop itself.
48 */
49public class Finalizer extends Thread {
50
51  private static final Logger logger
52      = Logger.getLogger(Finalizer.class.getName());
53
54  /** Name of FinalizableReference.class. */
55  private static final String FINALIZABLE_REFERENCE
56      = "com.google.common.base.FinalizableReference";
57
58  /**
59   * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
60   * reflectively.
61   *
62   * @param finalizableReferenceClass FinalizableReference.class
63   * @param frq reference to instance of FinalizableReferenceQueue that started
64   *  this thread
65   * @return ReferenceQueue which Finalizer will poll
66   */
67  public static ReferenceQueue<Object> startFinalizer(
68      Class<?> finalizableReferenceClass, Object frq) {
69    /*
70     * We use FinalizableReference.class for two things:
71     *
72     * 1) To invoke FinalizableReference.finalizeReferent()
73     *
74     * 2) To detect when FinalizableReference's class loader has to be garbage
75     * collected, at which point, Finalizer can stop running
76     */
77    if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
78      throw new IllegalArgumentException(
79          "Expected " + FINALIZABLE_REFERENCE + ".");
80    }
81
82    Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq);
83    finalizer.start();
84    return finalizer.queue;
85  }
86
87  private final WeakReference<Class<?>> finalizableReferenceClassReference;
88  private final PhantomReference<Object> frqReference;
89  private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
90
91  private static final Field inheritableThreadLocals
92      = getInheritableThreadLocalsField();
93
94  /** Constructs a new finalizer thread. */
95  private Finalizer(Class<?> finalizableReferenceClass, Object frq) {
96    super(Finalizer.class.getName());
97
98    this.finalizableReferenceClassReference
99        = new WeakReference<Class<?>>(finalizableReferenceClass);
100
101    // Keep track of the FRQ that started us so we know when to stop.
102    this.frqReference = new PhantomReference<Object>(frq, queue);
103
104    setDaemon(true);
105
106    try {
107      if (inheritableThreadLocals != null) {
108        inheritableThreadLocals.set(this, null);
109      }
110    } catch (Throwable t) {
111      logger.log(Level.INFO, "Failed to clear thread local values inherited"
112          + " by reference finalizer thread.", t);
113    }
114
115    // TODO: Priority?
116  }
117
118  /**
119   * Loops continuously, pulling references off the queue and cleaning them up.
120   */
121  @SuppressWarnings("InfiniteLoopStatement")
122  @Override
123  public void run() {
124    try {
125      while (true) {
126        try {
127          cleanUp(queue.remove());
128        } catch (InterruptedException e) { /* ignore */ }
129      }
130    } catch (ShutDown shutDown) { /* ignore */ }
131  }
132
133  /**
134   * Cleans up a single reference. Catches and logs all throwables.
135   */
136  private void cleanUp(Reference<?> reference) throws ShutDown {
137    Method finalizeReferentMethod = getFinalizeReferentMethod();
138    do {
139      /*
140       * This is for the benefit of phantom references. Weak and soft
141       * references will have already been cleared by this point.
142       */
143      reference.clear();
144
145      if (reference == frqReference) {
146        /*
147         * The client no longer has a reference to the
148         * FinalizableReferenceQueue. We can stop.
149         */
150        throw new ShutDown();
151      }
152
153      try {
154        finalizeReferentMethod.invoke(reference);
155      } catch (Throwable t) {
156        logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
157      }
158
159      /*
160       * Loop as long as we have references available so as not to waste
161       * CPU looking up the Method over and over again.
162       */
163    } while ((reference = queue.poll()) != null);
164  }
165
166  /**
167   * Looks up FinalizableReference.finalizeReferent() method.
168   */
169  private Method getFinalizeReferentMethod() throws ShutDown {
170    Class<?> finalizableReferenceClass
171        = finalizableReferenceClassReference.get();
172    if (finalizableReferenceClass == null) {
173      /*
174       * FinalizableReference's class loader was reclaimed. While there's a
175       * chance that other finalizable references could be enqueued
176       * subsequently (at which point the class loader would be resurrected
177       * by virtue of us having a strong reference to it), we should pretty
178       * much just shut down and make sure we don't keep it alive any longer
179       * than necessary.
180       */
181      throw new ShutDown();
182    }
183    try {
184      return finalizableReferenceClass.getMethod("finalizeReferent");
185    } catch (NoSuchMethodException e) {
186      throw new AssertionError(e);
187    }
188  }
189
190  public static Field getInheritableThreadLocalsField() {
191    try {
192      Field inheritableThreadLocals
193          = Thread.class.getDeclaredField("inheritableThreadLocals");
194      inheritableThreadLocals.setAccessible(true);
195      return inheritableThreadLocals;
196    } catch (Throwable t) {
197      logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
198          + " Reference finalizer threads will inherit thread local"
199          + " values.");
200      return null;
201    }
202  }
203
204  /** Indicates that it's time to shut down the Finalizer. */
205  @SuppressWarnings("serial") // Never serialized or thrown out of this class.
206  private static class ShutDown extends Exception { }
207}
208