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 jdk.internal.misc.InnocuousThread;
  29 
  30 import java.lang.ref.Cleaner;
  31 import java.lang.ref.ReferenceQueue;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 import java.util.Objects;
  35 import java.util.concurrent.ThreadFactory;
  36 import java.util.concurrent.atomic.AtomicInteger;
  37 
  38 /**
  39  * CleanerImpl is the implementation of {@link Cleaner}.
  40  */
  41 public class CleanerImpl implements Cleaner {
  42 
  43     final Task task;
  44 
  45     public CleanerImpl(ThreadFactory threadFactory) {
  46         task = new Task();
  47         task.start(this, threadFactory);
  48     }
  49 
  50     @Override
  51     public Cleanable register(Object obj, Runnable action) {
  52         Objects.requireNonNull(obj, "obj");
  53         Objects.requireNonNull(action, "action");
  54         return new CleanerImpl.PhantomCleanableRef(obj, this, action);
  55     }
  56 
  57     // package-private access to Task's state
  58     PhantomCleanable<?> phantomCleanableList() { return task.phantomCleanableList; }
  59     WeakCleanable<?> weakCleanableList() { return task.weakCleanableList; }
  60     SoftCleanable<?> softCleanableList() { return task.softCleanableList; }
  61     ReferenceQueue<Object> queue() { return task.queue; }
  62 
  63     /**
  64      * CleanerImpl.Task manages a set of object references and corresponding
  65      * cleaning actions and executes them after they are enqueued.
  66      */
  67     private static final class Task implements Runnable {
  68         /**
  69          * Heads of a CleanableList for each reference type.
  70          */
  71         final PhantomCleanable<?> phantomCleanableList;
  72 
  73         final WeakCleanable<?> weakCleanableList;
  74 
  75         final SoftCleanable<?> softCleanableList;
  76 
  77         // The ReferenceQueue of pending cleaning actions
  78         final ReferenceQueue<Object> queue;
  79 
  80         /**
  81          * Constructor for Task.
  82          */
  83         Task() {
  84             queue = new ReferenceQueue<>();
  85             phantomCleanableList = new PhantomCleanableRef();
  86             weakCleanableList = new WeakCleanableRef();
  87             softCleanableList = new SoftCleanableRef();
  88         }
  89 
  90         /**
  91          * Starts the Cleaner implementation.
  92          * Ensure this is the CleanerImpl for the Cleaner.
  93          * When started waits for Cleanables to be queued.
  94          * @param cleaner the cleaner
  95          * @param threadFactory the thread factory
  96          */
  97         void start(CleanerImpl cleaner, ThreadFactory threadFactory) {
  98             if (cleaner.task != this) {
  99                 throw new AssertionError("wrong cleaner");
 100             }
 101             // schedule a nop cleaning action for the cleaner, so the associated thread
 102             // will continue to run at least until the cleaner is reclaimable.
 103             new CleanerCleanable(cleaner);
 104 
 105             if (threadFactory == null) {
 106                 threadFactory = CleanerImpl.InnocuousThreadFactory.factory();
 107             }
 108 
 109             // now that there's at least one cleaning action, for the cleaner,
 110             // we can start the associated thread, which runs until
 111             // all cleaning actions have been run.
 112             Thread thread = threadFactory.newThread(this);
 113             thread.setDaemon(true);
 114             thread.start();
 115         }
 116 
 117         /**
 118          * Process queued Cleanables as long as the cleanable lists are not empty.
 119          * A Cleanable is in one of the lists for each Object and for the Cleaner
 120          * itself.
 121          * Terminates when the Cleaner is no longer reachable and
 122          * has been cleaned and there are no more Cleanable instances
 123          * for which the object is reachable.
 124          * <p>
 125          * If the thread is a ManagedLocalsThread, the threadlocals
 126          * are erased before each cleanup
 127          */
 128         @Override
 129         public void run() {
 130             Thread t = Thread.currentThread();
 131             InnocuousThread mlThread = (t instanceof InnocuousThread)
 132                     ? (InnocuousThread) t
 133                     : null;
 134             while (!phantomCleanableList.isListEmpty() ||
 135                     !weakCleanableList.isListEmpty() ||
 136                     !softCleanableList.isListEmpty()) {
 137                 if (mlThread != null) {
 138                     // Clear the thread locals
 139                     mlThread.eraseThreadLocals();
 140                 }
 141                 try {
 142                     // Wait for a Ref, with a timeout to avoid getting hung
 143                     // due to a race with clear/clean
 144                     Cleanable ref = (Cleanable) queue.remove(60 * 1000L);
 145                     if (ref != null) {
 146                         ref.clean();
 147                     }
 148                 } catch (Throwable e) {
 149                     // ignore exceptions from the cleanup action
 150                     // (including interruption of cleanup thread)
 151                 }
 152             }
 153         }
 154     }
 155 
 156     /**
 157      * Perform cleaning on an unreachable PhantomReference.
 158      */
 159     public static final class PhantomCleanableRef extends PhantomCleanable<Object> {
 160         private final Runnable action;
 161 
 162         /**
 163          * Constructor for a phantom cleanable reference.
 164          * @param obj the object to monitor
 165          * @param cleaner the cleaner
 166          * @param action the action Runnable
 167          */
 168         public PhantomCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
 169             super(obj, cleaner);
 170             this.action = action;
 171         }
 172 
 173         /**
 174          * Constructor used only for root of phantom cleanable list.
 175          */
 176         PhantomCleanableRef() {
 177             super();
 178             this.action = null;
 179         }
 180 
 181         @Override
 182         protected void performCleanup() {
 183             action.run();
 184         }
 185 
 186         /**
 187          * Prevent access to referent even when it is still alive.
 188          *
 189          * @throws UnsupportedOperationException always
 190          */
 191         @Override
 192         public Object get() {
 193             throw new UnsupportedOperationException("get");
 194         }
 195 
 196         /**
 197          * Direct clearing of the referent is not supported.
 198          *
 199          * @throws UnsupportedOperationException always
 200          */
 201         @Override
 202         public void clear() {
 203             throw new UnsupportedOperationException("clear");
 204         }
 205     }
 206 
 207     /**
 208      * Perform cleaning on an unreachable WeakReference.
 209      */
 210     public static final class WeakCleanableRef extends WeakCleanable<Object> {
 211         private final Runnable action;
 212 
 213         /**
 214          * Constructor for a weak cleanable reference.
 215          * @param obj the object to monitor
 216          * @param cleaner the cleaner
 217          * @param action the action Runnable
 218          */
 219         WeakCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
 220             super(obj, cleaner);
 221             this.action = action;
 222         }
 223 
 224         /**
 225          * Constructor used only for root of weak cleanable list.
 226          */
 227         WeakCleanableRef() {
 228             super();
 229             this.action = null;
 230         }
 231 
 232         @Override
 233         protected void performCleanup() {
 234             action.run();
 235         }
 236 
 237         /**
 238          * Prevent access to referent even when it is still alive.
 239          *
 240          * @throws UnsupportedOperationException always
 241          */
 242         @Override
 243         public Object get() {
 244             throw new UnsupportedOperationException("get");
 245         }
 246 
 247         /**
 248          * Direct clearing of the referent is not supported.
 249          *
 250          * @throws UnsupportedOperationException always
 251          */
 252         @Override
 253         public void clear() {
 254             throw new UnsupportedOperationException("clear");
 255         }
 256     }
 257 
 258     /**
 259      * Perform cleaning on an unreachable SoftReference.
 260      */
 261     public static final class SoftCleanableRef extends SoftCleanable<Object> {
 262         private final Runnable action;
 263 
 264         /**
 265          * Constructor for a soft cleanable reference.
 266          * @param obj the object to monitor
 267          * @param cleaner the cleaner
 268          * @param action the action Runnable
 269          */
 270         SoftCleanableRef(Object obj, Cleaner cleaner, Runnable action) {
 271             super(obj, cleaner);
 272             this.action = action;
 273         }
 274 
 275         /**
 276          * Constructor used only for root of soft cleanable list.
 277          */
 278         SoftCleanableRef() {
 279             super();
 280             this.action = null;
 281         }
 282 
 283         @Override
 284         protected void performCleanup() {
 285             action.run();
 286         }
 287 
 288         /**
 289          * Prevent access to referent even when it is still alive.
 290          *
 291          * @throws UnsupportedOperationException always
 292          */
 293         @Override
 294         public Object get() {
 295             throw new UnsupportedOperationException("get");
 296         }
 297 
 298         /**
 299          * Direct clearing of the referent is not supported.
 300          *
 301          * @throws UnsupportedOperationException always
 302          */
 303         @Override
 304         public void clear() {
 305             throw new UnsupportedOperationException("clear");
 306         }
 307 
 308     }
 309 
 310     /**
 311      * A ThreadFactory for InnocuousThreads.
 312      * The factory is a singleton.
 313      */
 314     static final class InnocuousThreadFactory implements ThreadFactory {
 315         final static ThreadFactory factory = new InnocuousThreadFactory();
 316 
 317         static ThreadFactory factory() {
 318             return factory;
 319         }
 320 
 321         final AtomicInteger cleanerThreadNumber = new AtomicInteger();
 322 
 323         public Thread newThread(Runnable r) {
 324             return AccessController.doPrivileged(new PrivilegedAction<>() {
 325                 @Override
 326                 public Thread run() {
 327                     Thread t = InnocuousThread.newThread(r);
 328                     t.setPriority(Thread.MAX_PRIORITY - 2);
 329                     t.setName("Cleaner-" + cleanerThreadNumber.getAndIncrement());
 330                     return t;
 331                 }
 332             });
 333         }
 334     }
 335 
 336     /**
 337      * A PhantomCleanable implementation for tracking the Cleaner itself.
 338      */
 339     static final class CleanerCleanable extends PhantomCleanable<Cleaner> {
 340         CleanerCleanable(Cleaner cleaner) {
 341             super(cleaner, cleaner);
 342         }
 343 
 344         @Override
 345         protected void performCleanup() {
 346             // no action
 347         }
 348     }
 349 }