1/*
2 * Copyright (c) 1997, 2015, 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
26package java.lang.ref;
27
28import java.security.PrivilegedAction;
29import java.security.AccessController;
30import jdk.internal.misc.JavaLangAccess;
31import jdk.internal.misc.SharedSecrets;
32import jdk.internal.misc.VM;
33
34final 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 ReferenceQueue<>();
39    private static Finalizer unfinalized = null;
40    private static final Object lock = new Object();
41
42    private Finalizer
43        next = null,
44        prev = null;
45
46    private boolean hasBeenFinalized() {
47        return (next == this);
48    }
49
50    private void add() {
51        synchronized (lock) {
52            if (unfinalized != null) {
53                this.next = unfinalized;
54                unfinalized.prev = this;
55            }
56            unfinalized = this;
57        }
58    }
59
60    private void remove() {
61        synchronized (lock) {
62            if (unfinalized == this) {
63                if (this.next != null) {
64                    unfinalized = this.next;
65                } else {
66                    unfinalized = this.prev;
67                }
68            }
69            if (this.next != null) {
70                this.next.prev = this.prev;
71            }
72            if (this.prev != null) {
73                this.prev.next = this.next;
74            }
75            this.next = this;   /* Indicates that this has been finalized */
76            this.prev = this;
77        }
78    }
79
80    private Finalizer(Object finalizee) {
81        super(finalizee, queue);
82        add();
83    }
84
85    static ReferenceQueue<Object> getQueue() {
86        return queue;
87    }
88
89    /* Invoked by VM */
90    static void register(Object finalizee) {
91        new Finalizer(finalizee);
92    }
93
94    private void runFinalizer(JavaLangAccess jla) {
95        synchronized (this) {
96            if (hasBeenFinalized()) return;
97            remove();
98        }
99        try {
100            Object finalizee = this.get();
101            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
102                jla.invokeFinalize(finalizee);
103
104                /* Clear stack slot containing this variable, to decrease
105                   the chances of false retention with a conservative GC */
106                finalizee = null;
107            }
108        } catch (Throwable x) { }
109        super.clear();
110    }
111
112    /* Create a privileged secondary finalizer thread in the system thread
113       group for the given Runnable, and wait for it to complete.
114
115       This method is used by both runFinalization and runFinalizersOnExit.
116       The former method invokes all pending finalizers, while the latter
117       invokes all uninvoked finalizers if on-exit finalization has been
118       enabled.
119
120       These two methods could have been implemented by offloading their work
121       to the regular finalizer thread and waiting for that thread to finish.
122       The advantage of creating a fresh thread, however, is that it insulates
123       invokers of these methods from a stalled or deadlocked finalizer thread.
124     */
125    private static void forkSecondaryFinalizer(final Runnable proc) {
126        AccessController.doPrivileged(
127            new PrivilegedAction<>() {
128                public Void run() {
129                    ThreadGroup tg = Thread.currentThread().getThreadGroup();
130                    for (ThreadGroup tgn = tg;
131                         tgn != null;
132                         tg = tgn, tgn = tg.getParent());
133                    Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false);
134                    sft.start();
135                    try {
136                        sft.join();
137                    } catch (InterruptedException x) {
138                        Thread.currentThread().interrupt();
139                    }
140                    return null;
141                }});
142    }
143
144    /* Called by Runtime.runFinalization() */
145    static void runFinalization() {
146        if (VM.initLevel() == 0) {
147            return;
148        }
149
150        forkSecondaryFinalizer(new Runnable() {
151            private volatile boolean running;
152            public void run() {
153                // in case of recursive call to run()
154                if (running)
155                    return;
156                final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
157                running = true;
158                for (;;) {
159                    Finalizer f = (Finalizer)queue.poll();
160                    if (f == null) break;
161                    f.runFinalizer(jla);
162                }
163            }
164        });
165    }
166
167    /* Invoked by java.lang.Shutdown */
168    static void runAllFinalizers() {
169        if (VM.initLevel() == 0) {
170            return;
171        }
172
173        forkSecondaryFinalizer(new Runnable() {
174            private volatile boolean running;
175            public void run() {
176                // in case of recursive call to run()
177                if (running)
178                    return;
179                final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
180                running = true;
181                for (;;) {
182                    Finalizer f;
183                    synchronized (lock) {
184                        f = unfinalized;
185                        if (f == null) break;
186                        unfinalized = f.next;
187                    }
188                    f.runFinalizer(jla);
189                }}});
190    }
191
192    private static class FinalizerThread extends Thread {
193        private volatile boolean running;
194        FinalizerThread(ThreadGroup g) {
195            super(g, null, "Finalizer", 0, false);
196        }
197        public void run() {
198            // in case of recursive call to run()
199            if (running)
200                return;
201
202            // Finalizer thread starts before System.initializeSystemClass
203            // is called.  Wait until JavaLangAccess is available
204            while (VM.initLevel() == 0) {
205                // delay until VM completes initialization
206                try {
207                    VM.awaitInitLevel(1);
208                } catch (InterruptedException x) {
209                    // ignore and continue
210                }
211            }
212            final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
213            running = true;
214            for (;;) {
215                try {
216                    Finalizer f = (Finalizer)queue.remove();
217                    f.runFinalizer(jla);
218                } catch (InterruptedException x) {
219                    // ignore and continue
220                }
221            }
222        }
223    }
224
225    static {
226        ThreadGroup tg = Thread.currentThread().getThreadGroup();
227        for (ThreadGroup tgn = tg;
228             tgn != null;
229             tg = tgn, tgn = tg.getParent());
230        Thread finalizer = new FinalizerThread(tg);
231        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
232        finalizer.setDaemon(true);
233        finalizer.start();
234    }
235
236}
237