001    /*
002     * Copyright (C) 2008 The Guava Authors
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.base.internal;
018    
019    import java.lang.ref.PhantomReference;
020    import java.lang.ref.Reference;
021    import java.lang.ref.ReferenceQueue;
022    import java.lang.ref.WeakReference;
023    import java.lang.reflect.Field;
024    import java.lang.reflect.Method;
025    import java.util.logging.Level;
026    import java.util.logging.Logger;
027    
028    /**
029     * Thread that finalizes referents. All references should implement
030     * {@code com.google.common.base.FinalizableReference}.
031     *
032     * <p>While this class is public, we consider it to be *internal* and not part
033     * of our published API. It is public so we can access it reflectively across
034     * class loaders in secure environments.
035     *
036     * <p>This class can't depend on other Google Collections code. If we were
037     * to load this class in the same class loader as the rest of
038     * Google Collections, this thread would keep an indirect strong reference
039     * to the class loader and prevent it from being garbage collected. This
040     * poses a problem for environments where you want to throw away the class
041     * loader. For example, dynamically reloading a web application or unloading
042     * an OSGi bundle.
043     *
044     * <p>{@code com.google.common.base.FinalizableReferenceQueue} loads this class
045     * in its own class loader. That way, this class doesn't prevent the main
046     * class loader from getting garbage collected, and this class can detect when
047     * the main class loader has been garbage collected and stop itself.
048     *
049     * @deprecated FinalizableReferenceQueue is an unsound mechanism for cleaning up references,
050     *     because (1) it's single thread can be easily overloaded, and (2) it's insistance on running
051     *     a background thread is problematic in certain environments. <b>This class is scheduled for
052     *     deletion in December 2012.</b>
053     */
054    @Deprecated
055    public
056    class Finalizer extends Thread {
057    
058      private static final Logger logger
059          = Logger.getLogger(Finalizer.class.getName());
060    
061      /** Name of FinalizableReference.class. */
062      private static final String FINALIZABLE_REFERENCE
063          = "com.google.common.base.FinalizableReference";
064    
065      /**
066       * Starts the Finalizer thread. FinalizableReferenceQueue calls this method
067       * reflectively.
068       *
069       * @param finalizableReferenceClass FinalizableReference.class
070       * @param frq reference to instance of FinalizableReferenceQueue that started
071       *  this thread
072       * @return ReferenceQueue which Finalizer will poll
073       */
074      public static ReferenceQueue<Object> startFinalizer(
075          Class<?> finalizableReferenceClass, Object frq) {
076        /*
077         * We use FinalizableReference.class for two things:
078         *
079         * 1) To invoke FinalizableReference.finalizeReferent()
080         *
081         * 2) To detect when FinalizableReference's class loader has to be garbage
082         * collected, at which point, Finalizer can stop running
083         */
084        if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) {
085          throw new IllegalArgumentException(
086              "Expected " + FINALIZABLE_REFERENCE + ".");
087        }
088    
089        Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq);
090        finalizer.start();
091        return finalizer.queue;
092      }
093    
094      private final WeakReference<Class<?>> finalizableReferenceClassReference;
095      private final PhantomReference<Object> frqReference;
096      private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
097    
098      private static final Field inheritableThreadLocals
099          = getInheritableThreadLocalsField();
100    
101      /** Constructs a new finalizer thread. */
102      private Finalizer(Class<?> finalizableReferenceClass, Object frq) {
103        super(Finalizer.class.getName());
104    
105        this.finalizableReferenceClassReference
106            = new WeakReference<Class<?>>(finalizableReferenceClass);
107    
108        // Keep track of the FRQ that started us so we know when to stop.
109        this.frqReference = new PhantomReference<Object>(frq, queue);
110    
111        setDaemon(true);
112    
113        try {
114          if (inheritableThreadLocals != null) {
115            inheritableThreadLocals.set(this, null);
116          }
117        } catch (Throwable t) {
118          logger.log(Level.INFO, "Failed to clear thread local values inherited"
119              + " by reference finalizer thread.", t);
120        }
121    
122        // TODO(fry): Priority?
123      }
124    
125      /**
126       * Loops continuously, pulling references off the queue and cleaning them up.
127       */
128      @SuppressWarnings("InfiniteLoopStatement")
129      @Override
130      public void run() {
131        try {
132          while (true) {
133            try {
134              cleanUp(queue.remove());
135            } catch (InterruptedException e) { /* ignore */ }
136          }
137        } catch (ShutDown shutDown) { /* ignore */ }
138      }
139    
140      /**
141       * Cleans up a single reference. Catches and logs all throwables.
142       */
143      private void cleanUp(Reference<?> reference) throws ShutDown {
144        Method finalizeReferentMethod = getFinalizeReferentMethod();
145        do {
146          /*
147           * This is for the benefit of phantom references. Weak and soft
148           * references will have already been cleared by this point.
149           */
150          reference.clear();
151    
152          if (reference == frqReference) {
153            /*
154             * The client no longer has a reference to the
155             * FinalizableReferenceQueue. We can stop.
156             */
157            throw new ShutDown();
158          }
159    
160          try {
161            finalizeReferentMethod.invoke(reference);
162          } catch (Throwable t) {
163            logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
164          }
165    
166          /*
167           * Loop as long as we have references available so as not to waste
168           * CPU looking up the Method over and over again.
169           */
170        } while ((reference = queue.poll()) != null);
171      }
172    
173      /**
174       * Looks up FinalizableReference.finalizeReferent() method.
175       */
176      private Method getFinalizeReferentMethod() throws ShutDown {
177        Class<?> finalizableReferenceClass
178            = finalizableReferenceClassReference.get();
179        if (finalizableReferenceClass == null) {
180          /*
181           * FinalizableReference's class loader was reclaimed. While there's a
182           * chance that other finalizable references could be enqueued
183           * subsequently (at which point the class loader would be resurrected
184           * by virtue of us having a strong reference to it), we should pretty
185           * much just shut down and make sure we don't keep it alive any longer
186           * than necessary.
187           */
188          throw new ShutDown();
189        }
190        try {
191          return finalizableReferenceClass.getMethod("finalizeReferent");
192        } catch (NoSuchMethodException e) {
193          throw new AssertionError(e);
194        }
195      }
196    
197      public static Field getInheritableThreadLocalsField() {
198        try {
199          Field inheritableThreadLocals
200              = Thread.class.getDeclaredField("inheritableThreadLocals");
201          inheritableThreadLocals.setAccessible(true);
202          return inheritableThreadLocals;
203        } catch (Throwable t) {
204          logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals."
205              + " Reference finalizer threads will inherit thread local"
206              + " values.");
207          return null;
208        }
209      }
210    
211      /** Indicates that it's time to shut down the Finalizer. */
212      @SuppressWarnings("serial") // Never serialized or thrown out of this class.
213      private static class ShutDown extends Exception { }
214    }