1 /*
  2  * Copyright (c) 1997, 2021, 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 java.lang.ref;
 27 
 28 import java.security.PrivilegedAction;
 29 import java.security.AccessController;
 30 import jdk.internal.access.JavaLangAccess;
 31 import jdk.internal.access.SharedSecrets;
 32 import jdk.internal.misc.VM;
 33 
 34 final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
 35                                                           same package as the Reference
 36                                                           class */
 37 
 38     private static ReferenceQueue<Object> queue = new NativeReferenceQueue<>();
 39 
 40     /** Head of doubly linked list of Finalizers awaiting finalization. */
 41     private static Finalizer unfinalized = null;
 42 
 43     /** Lock guarding access to unfinalized list. */
 44     private static final Object lock = new Object();
 45 
 46     private Finalizer next, prev;
 47 
 48     private Finalizer(Object finalizee) {
 49         super(finalizee, queue);
 50         // push onto unfinalized
 51         synchronized (lock) {
 52             if (unfinalized != null) {
 53                 this.next = unfinalized;
 54                 unfinalized.prev = this;
 55             }
 56             unfinalized = this;
 57         }
 58     }
 59 
 60     static ReferenceQueue<Object> getQueue() {
 61         return queue;
 62     }
 63 
 64     static final boolean ENABLED = isFinalizationEnabled();
 65 
 66     private static native boolean isFinalizationEnabled();
 67 
 68     /* Invoked by VM */
 69     static void register(Object finalizee) {
 70         if (ENABLED) {
 71             new Finalizer(finalizee);
 72         } else {
 73             throw new InternalError("unexpected call to Finalizer::register when finalization is disabled");
 74         }
 75     }
 76 
 77     private void runFinalizer(JavaLangAccess jla) {
 78         synchronized (lock) {
 79             if (this.next == this)      // already finalized
 80                 return;
 81             // unlink from unfinalized
 82             if (unfinalized == this)
 83                 unfinalized = this.next;
 84             else
 85                 this.prev.next = this.next;
 86             if (this.next != null)
 87                 this.next.prev = this.prev;
 88             this.prev = null;
 89             this.next = this;           // mark as finalized
 90         }
 91 
 92         try {
 93             Object finalizee = this.get();
 94             assert finalizee != null;
 95             if (!(finalizee instanceof java.lang.Enum)) {
 96                 jla.invokeFinalize(finalizee);
 97                 reportComplete(finalizee);
 98 
 99                 // Clear stack slot containing this variable, to decrease
100                 // the chances of false retention with a conservative GC
101                 finalizee = null;
102             }
103         } catch (Throwable x) { }
104         super.clear();
105     }
106 
107     private static native void reportComplete(Object finalizee);
108 
109     /* Create a privileged secondary finalizer thread in the system thread
110      * group for the given Runnable, and wait for it to complete.
111      *
112      * This method is used by runFinalization.
113      *
114      * It could have been implemented by offloading the work to the
115      * regular finalizer thread and waiting for that thread to finish.
116      * The advantage of creating a fresh thread, however, is that it insulates
117      * invokers of that method from a stalled or deadlocked finalizer thread.
118      */
119     @SuppressWarnings("removal")
120     private static void forkSecondaryFinalizer(final Runnable proc) {
121         AccessController.doPrivileged(
122             new PrivilegedAction<>() {
123                 public Void run() {
124                     @SuppressWarnings("deprecation")
125                     ThreadGroup tg = Thread.currentThread().getThreadGroup();
126                     for (ThreadGroup tgn = tg;
127                          tgn != null;
128                          tg = tgn, tgn = tg.getParent());
129                     Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false);
130                     sft.start();
131                     try {
132                         sft.join();
133                     } catch (InterruptedException x) {
134                         Thread.currentThread().interrupt();
135                     }
136                     return null;
137                 }});
138     }
139 
140     /* Called by Runtime.runFinalization() */
141     static void runFinalization() {
142         if (VM.initLevel() == 0 || ! ENABLED) {
143             return;
144         }
145 
146         forkSecondaryFinalizer(new Runnable() {
147             private volatile boolean running;
148             public void run() {
149                 // in case of recursive call to run()
150                 if (running)
151                     return;
152                 final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
153                 running = true;
154                 for (Finalizer f; (f = (Finalizer)queue.poll()) != null; )
155                     f.runFinalizer(jla);
156             }
157         });
158     }
159 
160     private static class FinalizerThread extends Thread {
161         private volatile boolean running;
162         FinalizerThread(ThreadGroup g) {
163             super(g, null, "Finalizer", 0, false);
164         }
165         public void run() {
166             // in case of recursive call to run()
167             if (running)
168                 return;
169 
170             // Finalizer thread starts before System.initializeSystemClass
171             // is called.  Wait until JavaLangAccess is available
172             while (VM.initLevel() == 0) {
173                 // delay until VM completes initialization
174                 try {
175                     VM.awaitInitLevel(1);
176                 } catch (InterruptedException x) {
177                     // ignore and continue
178                 }
179             }
180             final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
181             running = true;
182             for (;;) {
183                 try {
184                     Finalizer f = (Finalizer)queue.remove();
185                     f.runFinalizer(jla);
186                 } catch (InterruptedException x) {
187                     // ignore and continue
188                 }
189             }
190         }
191     }
192 
193     static {
194         if (ENABLED) {
195             @SuppressWarnings("deprecation")
196             ThreadGroup tg = Thread.currentThread().getThreadGroup();
197             for (ThreadGroup tgn = tg;
198                  tgn != null;
199                  tg = tgn, tgn = tg.getParent());
200             Thread finalizer = new FinalizerThread(tg);
201             finalizer.setPriority(Thread.MAX_PRIORITY - 2);
202             finalizer.setDaemon(true);
203             finalizer.start();
204         }
205     }
206 
207 }
--- EOF ---