/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang.ref; import sun.misc.InnocuousThread; import sun.misc.ManagedLocalsThread; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Objects; import java.util.concurrent.ThreadFactory; /** * Cleaner manages a set of object references and corresponding cleanup functions. * Each Cleaner operates independently, managing the pending cleanup functions * and handling threading and termination when the Cleaner is no longer in use. * Registering an object reference and corresponding cleaning function returns * allows the cleaning function to be invoked explicitly * {@link Cleanable#clean invoked} or to be * {@link Cleanable#clear cleared}. * The most efficient use is to explicitly invoke the * {@link Cleanable#clean Cleanable.clean} method when the object * is closed or no longer needed. * The cleaning function is invoked at most once when the object is * no longer reachable unless it has already been explicitly cleaned or cleared. * Note that the cleaning function must not refer to the object being registered. * If so, the object will not become unreachable and the cleaning function * will not be invoked. *

* Cleaning functions are registered as: *

*

* The execution of the cleaning function is performed * by a thread associated with the Cleaner. * The thread runs until all registered cleaning functions have * executed and the Cleaner itself is reclaimed by the garbage collector. * Cleaning functions should be prepared to be executed concurrently with * other cleaning functions. *

* The behavior of the Cleaner during {@link System#exit(int) System.exit} * is implementation specific. No guarantees are made related * to whether cleaning functions are executed or not. * * @author Roger Riggs * @author Kim Barrett * @apiNote Typically the cleaning functions should be very quick to execute * and not block. If the cleaning function blocks it may delay processing * other cleaning functions using the same Cleaner. It is up to the application * to have mutually compatible cleaning functions on a Cleaner. */ public final class Cleaner { /** * The Cleaner implementation. */ final CleanerImpl impl; /** * Construct a Cleaner implementation and start it. */ private Cleaner() { impl = new CleanerImpl(); } /** * Return a new Cleaner. *

* The Cleaner creates a daemon thread to process the * unreachable objects and to invoke cleaning functions. *

* The Cleaner terminates when it is unreachable and all of the objects * registered are unreachable and corresponding Runnables are complete. * * @return a new Cleaner */ public static Cleaner create() { return create(InnocuousThreadFactory.factory()); } /** * Returns a new Cleaner using a ThreadFactory. * The cleaning functions are processed on a new Thread from the {@link ThreadFactory}. * The Thread is {@link Thread#start started} to process cleaning functions. * The factory should set the Thread attributes as needed for * priority, name, thread group, uncaught exception handler, etc. * The thread is set to be a {@link Thread#setDaemon(boolean) daemon thread}. * The Cleaner uses this daemon thread to process the * unreachable objects and to invoke cleaning functions. *

* The Cleaner terminates when it is unreachable and all of the objects * registered are unreachable and corresponding Runnables are complete. * * @param threadFactory a Thread factory to return a new Thread to * process cleaning functions * @return a new Cleaner */ public static Cleaner create(ThreadFactory threadFactory) { Cleaner cleaner = new Cleaner(); cleaner.impl.start(cleaner, threadFactory); return cleaner; } /** * Registers an object and a Runnable to be run when the object * becomes phantom reachable. * Refer to the class javadoc for cautions about the behavior * of cleaning functions. * * @param obj the object to monitor * @param thunk a Runnable to invoke when the object becomes phantom reachable * @return a Cleanable instance */ public Cleanable phantomCleanable(Object obj, Runnable thunk) { Objects.requireNonNull(obj, "obj"); Objects.requireNonNull(thunk, "thunk"); return new PhantomCleanableRef(obj, this, thunk); } /** * Registers an object and a Runnable to be run when the object * becomes weakly reachable. * Refer to the class javadoc for cautions about the behavior * of cleaning functions. * * @param obj the object to monitor * @param thunk a Runnable to invoke when the object becomes weakly reachable * @return a Cleanable instance */ public Cleanable weakCleanable(Object obj, Runnable thunk) { Objects.requireNonNull(obj, "obj"); Objects.requireNonNull(thunk, "thunk"); return new WeakCleanableRef(obj, this, thunk); } /** * Registers an object and a Runnable to be run when the object * becomes softly reachable. * Refer to the class javadoc for cautions about the behavior * of cleaning functions. * * @param obj the object to monitorss * @param thunk a Runnable to invoke when the object becomes softly reachable * @return a Cleanable instance */ public Cleanable softCleanable(Object obj, Runnable thunk) { Objects.requireNonNull(obj, "obj"); Objects.requireNonNull(thunk, "thunk"); return new SoftCleanableRef(obj, this, thunk); } /** * CleanerImpl manages the thread and the pending cleanup functions. */ static final class CleanerImpl implements Runnable { // Head of the Cleanable list private final CleanableList cleanableList; // The queue of pending cleaning functions final ReferenceQueue queue; // The lock used to insert or remove a cleaning function from the cleanableList. private final Object lock; // Construct a CleanerImpl. CleanerImpl() { queue = new ReferenceQueue<>(); lock = new Object(); cleanableList = new CleanableList(); } /** * Starts the Cleaner implementation. * When started waits for Cleanables to be queued. */ void start(Cleaner service, ThreadFactory threadFactory) { // schedule a nop cleaning function for the service, so the associated thread // will continue to run at least until the service is reclaimable. new PhantomCleanableReference(service, service) { @Override protected void performCleanup() { } }; // now that there's at least one cleaning function, for the service, // we can start the associated thread, which runs until // all cleaning functions have been run. Thread thread = threadFactory.newThread(this); thread.setPriority(Thread.MAX_PRIORITY); thread.setDaemon(true); thread.start(); } /** * Insert given Cleanable after the cleanableList head. * * @param c the Cleanable to insert after cleanableList head */ void insert(Cleanable c) { synchronized (lock) { setPrev(c, cleanableList); Cleanable clNext = getNext(cleanableList); setNext(c, clNext); setPrev(clNext, c); setNext(cleanableList, c); } } /** * Remove given Cleanable from the cleanableList. * * @param c the Cleanable to remove from the cleanableList * @return true if Cleanable was removed or false if not because * it had already been removed before */ boolean remove(Cleanable c) { synchronized (lock) { if (getNext(c) != c) { setPrev(getNext(c), getPrev(c)); setNext(getPrev(c), getNext(c)); setPrev(c, c); setNext(c, c); return true; } return false; } } // "polymorphic" next/prev field accessors... // (wish there were package-private interface methods) private Cleanable getNext(Cleanable c) { if (c instanceof WeakCleanableReference) { return ((WeakCleanableReference)c).next; } else if (c instanceof SoftCleanableReference) { return ((SoftCleanableReference)c).next; } else if (c instanceof PhantomCleanableReference) { return ((PhantomCleanableReference)c).next; } else if (c instanceof CleanableList) { return ((CleanableList)c).next; } else { throw invalidCleanable(c); } } private void setNext(Cleanable c, Cleanable next) { if (c instanceof WeakCleanableReference) { ((WeakCleanableReference)c).next = next; } else if (c instanceof SoftCleanableReference) { ((SoftCleanableReference)c).next = next; } else if (c instanceof PhantomCleanableReference) { ((PhantomCleanableReference)c).next = next; } else if (c instanceof CleanableList) { ((CleanableList)c).next = next; } else { throw invalidCleanable(c); } } private Cleanable getPrev(Cleanable c) { if (c instanceof WeakCleanableReference) { return ((WeakCleanableReference)c).prev; } else if (c instanceof SoftCleanableReference) { return ((SoftCleanableReference)c).prev; } else if (c instanceof PhantomCleanableReference) { return ((PhantomCleanableReference)c).prev; } else if (c instanceof CleanableList) { return ((CleanableList)c).prev; } else { throw invalidCleanable(c); } } private void setPrev(Cleanable c, Cleanable prev) { if (c instanceof WeakCleanableReference) { ((WeakCleanableReference)c).prev = prev; } else if (c instanceof SoftCleanableReference) { ((SoftCleanableReference)c).prev = prev; } else if (c instanceof PhantomCleanableReference) { ((PhantomCleanableReference)c).prev = prev; } else if (c instanceof CleanableList) { ((CleanableList)c).prev = prev; } else { throw invalidCleanable(c); } } private static Error invalidCleanable(Cleanable c) { return new InternalError("Invalid Cleanable class: " + c.getClass().getName()); } /** * Process queued Cleanables as long as the cleanableList is not empty. * A Cleanable is in the list for each Object and for the Cleaner * itself. * Terminates when the Cleaner is no longer reachable and * has been cleaned and there are no more Cleanable instances * for which the object is reachable. *

* If the thread is a ManagedLocalsThread, the threadlocals * are erased before each cleanup */ public void run() { Thread t = Thread.currentThread(); ManagedLocalsThread mlThread = (t instanceof ManagedLocalsThread) ? (ManagedLocalsThread)t : null; while (!cleanableList.isEmpty()) { if (mlThread != null) { // Cleanable the thread locals mlThread.eraseThreadLocals(); } try { Cleanable ref = (Cleanable) queue.remove(); ref.clean(); } catch (InterruptedException i) { continue; // ignore the interruption } catch (RuntimeException e) { // ignore exceptions from the cleanup thunk } } } } /** * Cleanable instances allow a registered cleaning function to be cleaned or cleared. * Cleanable Runnables are invoked at most once. */ public interface Cleanable { /** * Unregisters the Cleanable. * Due to inherent concurrency, the cleaning function may still be invoked. */ void clear(); /** * Unregister the Cleanable and invoke the Runnable, * ensuring at-most-once semantics. */ void clean(); } /** * Perform cleaning on an unreachable PhantomReference. */ static final class PhantomCleanableRef extends PhantomCleanableReference { private final Runnable thunk; PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable thunk) { super(obj, cleaner); this.thunk = thunk; } @Override protected void performCleanup() { thunk.run(); } /** * Prevent access to referent even when it is still alive. * @throws UnsupportedOperationException always * @return nothing */ @Override public Object get() { throw new UnsupportedOperationException(); } } /** * Perform cleaning on an unreachable WeakReference. */ static final class WeakCleanableRef extends WeakCleanableReference { private final Runnable thunk; WeakCleanableRef(Object obj, Cleaner cleaner, Runnable thunk) { super(obj, cleaner); this.thunk = thunk; } @Override protected void performCleanup() { thunk.run(); } /** * Prevent access to referent even when it is still alive. * @throws UnsupportedOperationException always * @return nothing */ @Override public Object get() { throw new UnsupportedOperationException(); } } /** * Perform cleaning on an unreachable SoftReference. */ static final class SoftCleanableRef extends SoftCleanableReference { private final Runnable thunk; SoftCleanableRef(Object obj, Cleaner cleaner, Runnable thunk) { super(obj, cleaner); this.thunk = thunk; } @Override protected void performCleanup() { thunk.run(); } /** * Prevent access to referent even when it is still alive. * @throws UnsupportedOperationException always * @return nothing */ @Override public Object get() { throw new UnsupportedOperationException(); } } static final class CleanableList implements Cleanable { Cleanable prev = this, next = this; boolean isEmpty() { return next == this; } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public void clean() { throw new UnsupportedOperationException(); } } /** * A ThreadFactory for InnocuousThreads (the default). * The factory is a singleton. */ static class InnocuousThreadFactory implements ThreadFactory { final static ThreadFactory factory = new InnocuousThreadFactory(); static ThreadFactory factory() { return factory; } public Thread newThread(Runnable r) { return AccessController.doPrivileged((PrivilegedAction) () -> { Thread t = new InnocuousThread(r); t.setName("Cleaner-" + t.getId()); return t; }); } } }