001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.base.internal;
016
017import java.lang.ref.PhantomReference;
018import java.lang.ref.Reference;
019import java.lang.ref.ReferenceQueue;
020import java.lang.ref.WeakReference;
021import java.lang.reflect.Constructor;
022import java.lang.reflect.Field;
023import java.lang.reflect.Method;
024import java.util.logging.Level;
025import java.util.logging.Logger;
026import javax.annotation.CheckForNull;
027
028/**
029 * Thread that finalizes referents. All references should implement {@code
030 * com.google.common.base.FinalizableReference}.
031 *
032 * <p>While this class is public, we consider it to be *internal* and not part of our published API.
033 * It is public so we can access it reflectively across class loaders in secure environments.
034 *
035 * <p>This class can't depend on other Guava code. If we were to load this class in the same class
036 * loader as the rest of Guava, this thread would keep an indirect strong reference to the class
037 * loader and prevent it from being garbage collected. This poses a problem for environments where
038 * you want to throw away the class loader. For example, dynamically reloading a web application or
039 * unloading an OSGi bundle.
040 *
041 * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class in its own class
042 * loader. That way, this class doesn't prevent the main class loader from getting garbage
043 * collected, and this class can detect when the main class loader has been garbage collected and
044 * stop itself.
045 */
046// no @ElementTypesAreNonNullByDefault for the reasons discussed above
047public class Finalizer implements Runnable {
048
049  private static final Logger logger = Logger.getLogger(Finalizer.class.getName());
050
051  /** Name of FinalizableReference.class. */
052  private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference";
053
054  /**
055   * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively.
056   *
057   * @param finalizableReferenceClass FinalizableReference.class.
058   * @param queue a reference queue that the thread will poll.
059   * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued
060   *     either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its
061   *     close() method is called.
062   */
063  public static void startFinalizer(
064      Class<?> finalizableReferenceClass,
065      ReferenceQueue<Object> queue,
066      PhantomReference<Object> frqReference) {
067    /*
068     * We use FinalizableReference.class for two things:
069     *
070     * 1) To invoke FinalizableReference.finalizeReferent()
071     *
072     * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which
073     * point, Finalizer can stop running
074     */
075    if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
076      throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + ".");
077    }
078
079    Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference);
080    String threadName = Finalizer.class.getName();
081    Thread thread = null;
082    if (bigThreadConstructor != null) {
083      try {
084        boolean inheritThreadLocals = false;
085        long defaultStackSize = 0;
086        thread =
087            bigThreadConstructor.newInstance(
088                (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals);
089      } catch (Throwable t) {
090        logger.log(
091            Level.INFO, "Failed to create a thread without inherited thread-local values", t);
092      }
093    }
094    if (thread == null) {
095      thread = new Thread((ThreadGroup) null, finalizer, threadName);
096    }
097    thread.setDaemon(true);
098
099    try {
100      if (inheritableThreadLocals != null) {
101        inheritableThreadLocals.set(thread, null);
102      }
103    } catch (Throwable t) {
104      logger.log(
105          Level.INFO,
106          "Failed to clear thread local values inherited by reference finalizer thread.",
107          t);
108    }
109
110    thread.start();
111  }
112
113  private final WeakReference<Class<?>> finalizableReferenceClassReference;
114  private final PhantomReference<Object> frqReference;
115  private final ReferenceQueue<Object> queue;
116
117  // By preference, we will use the Thread constructor that has an `inheritThreadLocals` parameter.
118  // But before Java 9, our only way not to inherit ThreadLocals is to zap them after the thread
119  // is created, by accessing a private field.
120  @CheckForNull
121  private static final Constructor<Thread> bigThreadConstructor = getBigThreadConstructor();
122
123  @CheckForNull
124  private static final Field inheritableThreadLocals =
125      (bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null;
126
127  /** Constructs a new finalizer thread. */
128  private Finalizer(
129      Class<?> finalizableReferenceClass,
130      ReferenceQueue<Object> queue,
131      PhantomReference<Object> frqReference) {
132    this.queue = queue;
133
134    this.finalizableReferenceClassReference = new WeakReference<>(finalizableReferenceClass);
135
136    // Keep track of the FRQ that started us so we know when to stop.
137    this.frqReference = frqReference;
138  }
139
140  /** Loops continuously, pulling references off the queue and cleaning them up. */
141  @SuppressWarnings("InfiniteLoopStatement")
142  @Override
143  public void run() {
144    while (true) {
145      try {
146        if (!cleanUp(queue.remove())) {
147          break;
148        }
149      } catch (InterruptedException e) {
150        // ignore
151      }
152    }
153  }
154
155  /**
156   * Cleans up the given reference and any other references already in the queue. Catches and logs
157   * all throwables.
158   *
159   * @return true if the caller should continue to wait for more references to be added to the
160   *     queue, false if the associated FinalizableReferenceQueue is no longer referenced.
161   */
162  private boolean cleanUp(Reference<?> firstReference) {
163    Method finalizeReferentMethod = getFinalizeReferentMethod();
164    if (finalizeReferentMethod == null) {
165      return false;
166    }
167
168    if (!finalizeReference(firstReference, finalizeReferentMethod)) {
169      return false;
170    }
171
172    /*
173     * Loop as long as we have references available so as not to waste CPU looking up the Method
174     * over and over again.
175     */
176    while (true) {
177      Reference<?> furtherReference = queue.poll();
178      if (furtherReference == null) {
179        return true;
180      }
181      if (!finalizeReference(furtherReference, finalizeReferentMethod)) {
182        return false;
183      }
184    }
185  }
186
187  /**
188   * Cleans up the given reference. Catches and logs all throwables.
189   *
190   * @return true if the caller should continue to clean up references from the queue, false if the
191   *     associated FinalizableReferenceQueue is no longer referenced.
192   */
193  private boolean finalizeReference(Reference<?> reference, Method finalizeReferentMethod) {
194    /*
195     * This is for the benefit of phantom references. Weak and soft references will have already
196     * been cleared by this point.
197     */
198    reference.clear();
199
200    if (reference == frqReference) {
201      /*
202       * The client no longer has a reference to the FinalizableReferenceQueue. We can stop.
203       */
204      return false;
205    }
206
207    try {
208      finalizeReferentMethod.invoke(reference);
209    } catch (Throwable t) {
210      logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
211    }
212    return true;
213  }
214
215  /** Looks up FinalizableReference.finalizeReferent() method. */
216  @CheckForNull
217  private Method getFinalizeReferentMethod() {
218    Class<?> finalizableReferenceClass = finalizableReferenceClassReference.get();
219    if (finalizableReferenceClass == null) {
220      /*
221       * FinalizableReference's class loader was reclaimed. While there's a chance that other
222       * finalizable references could be enqueued subsequently (at which point the class loader
223       * would be resurrected by virtue of us having a strong reference to it), we should pretty
224       * much just shut down and make sure we don't keep it alive any longer than necessary.
225       */
226      return null;
227    }
228    try {
229      return finalizableReferenceClass.getMethod("finalizeReferent");
230    } catch (NoSuchMethodException e) {
231      throw new AssertionError(e);
232    }
233  }
234
235  @CheckForNull
236  private static Field getInheritableThreadLocalsField() {
237    try {
238      Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals");
239      inheritableThreadLocals.setAccessible(true);
240      return inheritableThreadLocals;
241    } catch (Throwable t) {
242      logger.log(
243          Level.INFO,
244          "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will "
245              + "inherit thread local values.");
246      return null;
247    }
248  }
249
250  @CheckForNull
251  private static Constructor<Thread> getBigThreadConstructor() {
252    try {
253      return Thread.class.getConstructor(
254          ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class);
255    } catch (Throwable t) {
256      // Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals.
257      return null;
258    }
259  }
260}