1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.ref;
  27 
  28 import java.lang.ref.Cleaner;
  29 import java.lang.ref.Cleaner.Cleanable;
  30 import java.lang.ref.ReferenceQueue;
  31 import java.security.AccessController;
  32 import java.security.PrivilegedAction;
  33 import java.util.concurrent.ThreadFactory;
  34 import java.util.function.Function;
  35 
  36 import sun.misc.InnocuousThread;
  37 
  38 /**
  39  * CleanerImpl manages a set of object references and corresponding cleaning actions.
  40  * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}.
  41  */
  42 public final class CleanerImpl {
  43 
  44     /**
  45      * An object to access the CleanerImpl from a Cleaner; set by Cleaner init.
  46      */
  47     private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null;
  48 
  49     /**
  50      * Heads of a CleanableList for each reference type.
  51      */
  52     final PhantomCleanable<?> phantomCleanableList;
  53 
  54     final WeakCleanable<?> weakCleanableList;
  55 
  56     final SoftCleanable<?> softCleanableList;
  57 
  58     // The ReferenceQueue of pending cleaning actions
  59     final ReferenceQueue<Object> queue;
  60 
  61     /**
  62      * Called by Cleaner static initialization to provide the function
  63      * to map from Cleaner to CleanerImpl.
  64      * @param access a function to map from Cleaner to CleanerImpl
  65      */
  66     public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) {
  67         if (cleanerImplAccess == null) {
  68             cleanerImplAccess = access;
  69         } else {
  70             throw new InternalError("cleanerImplAccess");
  71         }
  72     }
  73 
  74     /**
  75      * Called to get the CleanerImpl for a Cleaner.
  76      * @param cleaner the cleaner
  77      * @return the corresponding CleanerImpl
  78      */
  79     public static CleanerImpl getCleanerImpl(Cleaner cleaner) {
  80         return cleanerImplAccess.apply(cleaner);
  81     }
  82 
  83     /**
  84      * Constructor for CleanerImpl.
  85      */
  86     public CleanerImpl() {
  87         queue = new ReferenceQueue<>();
  88         phantomCleanableList = new PhantomCleanableRef();
  89         weakCleanableList = new WeakCleanableRef();
  90         softCleanableList = new SoftCleanableRef();
  91     }
  92 
  93     /**
  94      * Starts the Cleaner implementation.
  95      * Ensure this is the CleanerImpl for the Cleaner.
  96      * When started waits for Cleanables to be queued.
  97      * @param cleaner the cleaner
  98      * @param threadFactory the thread factory
  99      */
 100     public void start(Cleaner cleaner, ThreadFactory threadFactory) {
 101         if (getCleanerImpl(cleaner) != this) {
 102             throw new AssertionError("wrong cleaner");
 103         }
 104         // schedule a nop cleaning action for the cleaner, so the associated thread
 105         // will continue to run at least until the cleaner is reclaimable.
 106         new PhantomCleanableRef(cleaner, cleaner, () -> {});
 107 
 108         if (threadFactory == null) {
 109             threadFactory = CleanerImpl.InnocuousThreadFactory.factory();
 110         }
 111 
 112         // now that there's at least one cleaning action, for the cleaner,
 113         // we can start the associated thread, which runs until
 114         // all cleaning actions have been run.
 115         Thread thread = threadFactory.newThread(this::run);
 116         thread.setDaemon(true);
 117         thread.start();
 118     }
 119 
 120     /**
 121      * Process queued Cleanables as long as the cleanable lists are not empty.
 122      * A Cleanable is in one of the lists for each Object and for the Cleaner
 123      * itself.
 124      * Terminates when the Cleaner is no longer reachable and
 125      * has been cleaned and there are no more Cleanable instances
 126      * for which the object is reachable.
 127      * <p>
 128      * If the thread is a ManagedLocalsThread, the threadlocals
 129      * are erased before each cleanup
 130      */
 131     private void run() {
 132         Thread t = Thread.currentThread();
 133         InnocuousThread mlThread = (t instanceof InnocuousThread)
 134                 ? (InnocuousThread) t
 135                 : null;
 136         while (!phantomCleanableList.isListEmpty() ||
 137                 !weakCleanableList.isListEmpty() ||
 138                 !softCleanableList.isListEmpty()) {
 139             if (mlThread != null) {
 140                 // Clear the thread locals
 141                 mlThread.eraseThreadLocals();
 142             }
 143             try {
 144                 // Wait for a Ref, with a timeout to avoid getting hung
 145                 // due to a race with clear/clean
 146                 Cleanable ref = (Cleanable) queue.remove(60 * 1000L);
 147                 if (ref != null) {
 148                     ref.clean();
 149                 }
 150             } catch (Throwable e) {
 151                 // ignore exceptions from the cleanup action
 152                 // (including interruption of cleanup thread)
 153             }
 154         }
 155     }
 156 
 157     /**
 158      * Processes all Cleanable(s) that have been waiting in the queue.
 159      *
 160      * @return {@code true} if any Cleanable was found in the queue and
 161      * was processed or {@code false} if the queue was empty.
 162      */
 163     public boolean drainQueue() {
 164         boolean cleaned = false;
 165         Cleanable ref;
 166         while ((ref = (Cleanable) queue.poll()) != null) {
 167             try {
 168                 ref.clean();
 169             } catch (Throwable t) {
 170                 // ignore exceptions from the cleanup action
 171             }
 172             cleaned = true;
 173         }
 174         return cleaned;
 175     }
 176 
 177     /**
 178      * Perform cleaning on an unreachable PhantomReference.
 179      */
 180     public static final class PhantomCleanableRef extends PhantomCleanable<Object> {
 181         private final Runnable action;
 182 
 183         /**
 184          * Constructor for a phantom cleanable reference.
 185          * @param obj the object to monitor
 186          * @param cleaner the cleaner
 187          * @param action the action Runnable
 188          */
 189         public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
 190             super(obj, cleaner);
 191             this.action = action;
 192         }
 193 
 194         /**
 195          * Constructor used only for root of phantom cleanable list.
 196          */
 197         PhantomCleanableRef() {
 198             super();
 199             this.action = null;
 200         }
 201 
 202         @Override
 203         protected void performCleanup() {
 204             action.run();
 205         }
 206 
 207         /**
 208          * Prevent access to referent even when it is still alive.
 209          *
 210          * @throws UnsupportedOperationException always
 211          */
 212         @Override
 213         public Object get() {
 214             throw new UnsupportedOperationException("get");
 215         }
 216 
 217         /**
 218          * Direct clearing of the referent is not supported.
 219          *
 220          * @throws UnsupportedOperationException always
 221          */
 222         @Override
 223         public void clear() {
 224             throw new UnsupportedOperationException("clear");
 225         }
 226     }
 227 
 228     /**
 229      * Perform cleaning on an unreachable WeakReference.
 230      */
 231     public static final class WeakCleanableRef extends WeakCleanable<Object> {
 232         private final Runnable action;
 233 
 234         /**
 235          * Constructor for a weak cleanable reference.
 236          * @param obj the object to monitor
 237          * @param cleaner the cleaner
 238          * @param action the action Runnable
 239          */
 240         WeakCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
 241             super(obj, cleaner);
 242             this.action = action;
 243         }
 244 
 245         /**
 246          * Constructor used only for root of weak cleanable list.
 247          */
 248         WeakCleanableRef() {
 249             super();
 250             this.action = null;
 251         }
 252 
 253         @Override
 254         protected void performCleanup() {
 255             action.run();
 256         }
 257 
 258         /**
 259          * Prevent access to referent even when it is still alive.
 260          *
 261          * @throws UnsupportedOperationException always
 262          */
 263         @Override
 264         public Object get() {
 265             throw new UnsupportedOperationException("get");
 266         }
 267 
 268         /**
 269          * Direct clearing of the referent is not supported.
 270          *
 271          * @throws UnsupportedOperationException always
 272          */
 273         @Override
 274         public void clear() {
 275             throw new UnsupportedOperationException("clear");
 276         }
 277     }
 278 
 279     /**
 280      * Perform cleaning on an unreachable SoftReference.
 281      */
 282     public static final class SoftCleanableRef extends SoftCleanable<Object> {
 283         private final Runnable action;
 284 
 285         /**
 286          * Constructor for a soft cleanable reference.
 287          * @param obj the object to monitor
 288          * @param cleaner the cleaner
 289          * @param action the action Runnable
 290          */
 291         SoftCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
 292             super(obj, cleaner);
 293             this.action = action;
 294         }
 295 
 296         /**
 297          * Constructor used only for root of soft cleanable list.
 298          */
 299         SoftCleanableRef() {
 300             super();
 301             this.action = null;
 302         }
 303 
 304         @Override
 305         protected void performCleanup() {
 306             action.run();
 307         }
 308 
 309         /**
 310          * Prevent access to referent even when it is still alive.
 311          *
 312          * @throws UnsupportedOperationException always
 313          */
 314         @Override
 315         public Object get() {
 316             throw new UnsupportedOperationException("get");
 317         }
 318 
 319         /**
 320          * Direct clearing of the referent is not supported.
 321          *
 322          * @throws UnsupportedOperationException always
 323          */
 324         @Override
 325         public void clear() {
 326             throw new UnsupportedOperationException("clear");
 327         }
 328 
 329     }
 330 
 331     /**
 332      * A ThreadFactory for InnocuousThreads.
 333      * The factory is a singleton.
 334      */
 335     static final class InnocuousThreadFactory implements ThreadFactory {
 336         final static ThreadFactory factory = new InnocuousThreadFactory();
 337 
 338         static ThreadFactory factory() {
 339             return factory;
 340         }
 341 
 342         public Thread newThread(Runnable r) {
 343             return AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
 344                 Thread t = new InnocuousThread(r);
 345                 t.setPriority(Thread.MAX_PRIORITY - 2);
 346                 t.setName("Cleaner-" + t.getId());
 347                 return t;
 348             });
 349         }
 350     }
 351 
 352 }