1 /*
   2  * Copyright (c) 2003, 2013, 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.*;
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 
  32 
  33 /**
  34  * General-purpose phantom-reference-based cleaners.
  35  *
  36  * <p> Cleaners are a lightweight and more robust alternative to finalization.
  37  * They are lightweight because they are not created by the VM and thus do not
  38  * require a JNI upcall to be created, and because their cleanup code is
  39  * invoked directly by the reference-handler thread rather than by the
  40  * finalizer thread.  They are more robust because they use phantom references,
  41  * the weakest type of reference object, thereby avoiding the nasty ordering
  42  * problems inherent to finalization.
  43  *
  44  * <p> A cleaner tracks a referent object and encapsulates a thunk of arbitrary
  45  * cleanup code.  Some time after the GC detects that a cleaner's referent has
  46  * become phantom-reachable, the reference-handler thread will run the cleaner.
  47  * Cleaners may also be invoked directly; they are thread safe and ensure that
  48  * they run their thunks at most once.
  49  *
  50  * <p> Cleaners are not a replacement for finalization.  They should be used
  51  * only when the cleanup code is extremely simple and straightforward.
  52  * Nontrivial cleaners are inadvisable since they risk blocking the
  53  * reference-handler thread and delaying further cleanup and finalization.
  54  *
  55  *
  56  * @author Mark Reinhold
  57  */
  58 
  59 public class Cleaner
  60     extends PhantomReference<Object>
  61     implements Runnable
  62 {
  63 
  64     // Dummy reference queue, needed because the PhantomReference constructor
  65     // insists that we pass a queue.  Nothing will ever be placed on this queue
  66     // since the reference handler invokes cleaners explicitly.
  67     //
  68     private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();
  69 
  70     // Doubly-linked list of live cleaners, which prevents the cleaners
  71     // themselves from being GC'd before their referents
  72     //
  73     private static Cleaner first = null;
  74 
  75     private Cleaner
  76         next = null,
  77         prev = null;
  78 
  79     private static synchronized Cleaner add(Cleaner cl) {
  80         if (first != null) {
  81             cl.next = first;
  82             first.prev = cl;
  83         }
  84         first = cl;
  85         return cl;
  86     }
  87 
  88     private static synchronized boolean remove(Cleaner cl) {
  89 
  90         // If already removed, do nothing
  91         if (cl.next == cl)
  92             return false;
  93 
  94         // Update list
  95         if (first == cl) {
  96             if (cl.next != null)
  97                 first = cl.next;
  98             else
  99                 first = cl.prev;
 100         }
 101         if (cl.next != null)
 102             cl.next.prev = cl.prev;
 103         if (cl.prev != null)
 104             cl.prev.next = cl.next;
 105 
 106         // Indicate removal by pointing the cleaner to itself
 107         cl.next = cl;
 108         cl.prev = cl;
 109         return true;
 110 
 111     }
 112 
 113     private final Runnable thunk;
 114 
 115     private Cleaner(Object referent, Runnable thunk) {
 116         super(referent, dummyQueue);
 117         this.thunk = thunk;
 118     }
 119 
 120     /**
 121      * Creates a new cleaner.
 122      *
 123      * @param  ob the referent object to be cleaned
 124      * @param  thunk
 125      *         The cleanup code to be run when the cleaner is invoked.  The
 126      *         cleanup code is run directly from the reference-handler thread,
 127      *         so it should be as simple and straightforward as possible.
 128      *
 129      * @return  The new cleaner
 130      */
 131     public static Cleaner create(Object ob, Runnable thunk) {
 132         if (thunk == null)
 133             return null;
 134         return add(new Cleaner(ob, thunk));
 135     }
 136 
 137     /**
 138      * Runs this cleaner, if it has not been run before.
 139      */
 140     public void clean() {
 141         if (!remove(this))
 142             return;
 143         try {
 144             thunk.run();
 145         } catch (final Throwable x) {
 146             AccessController.doPrivileged(new PrivilegedAction<>() {
 147                     public Void run() {
 148                         if (System.err != null)
 149                             new Error("Cleaner terminated abnormally", x)
 150                                 .printStackTrace();
 151                         System.exit(1);
 152                         return null;
 153                     }});
 154         }
 155     }
 156 
 157     @Override public void run() {
 158         SecurityManager security = System.getSecurityManager();
 159         if (security != null)
 160             security.checkPackageAccess("jdk.internal.ref");
 161         this.clean();
 162     }
 163 
 164 }