1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.lang.ref;
18
19/**
20 * @hide
21 */
22public final class FinalizerReference<T> extends Reference<T> {
23    // This queue contains those objects eligible for finalization.
24    public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
25
26    // Guards the list (not the queue).
27    private static final Object LIST_LOCK = new Object();
28
29    // This list contains a FinalizerReference for every finalizable object in the heap.
30    // Objects in this list may or may not be eligible for finalization yet.
31    private static FinalizerReference<?> head = null;
32
33    // The links used to construct the list.
34    private FinalizerReference<?> prev;
35    private FinalizerReference<?> next;
36
37    // When the GC wants something finalized, it moves it from the 'referent' field to
38    // the 'zombie' field instead.
39    private T zombie;
40
41    public FinalizerReference(T r, ReferenceQueue<? super T> q) {
42        super(r, q);
43    }
44
45    @Override public T get() {
46        return zombie;
47    }
48
49    @Override public void clear() {
50        zombie = null;
51    }
52
53    public static void add(Object referent) {
54        FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
55        synchronized (LIST_LOCK) {
56            reference.prev = null;
57            reference.next = head;
58            if (head != null) {
59                head.prev = reference;
60            }
61            head = reference;
62        }
63    }
64
65    public static void remove(FinalizerReference<?> reference) {
66        synchronized (LIST_LOCK) {
67            FinalizerReference<?> next = reference.next;
68            FinalizerReference<?> prev = reference.prev;
69            reference.next = null;
70            reference.prev = null;
71            if (prev != null) {
72                prev.next = next;
73            } else {
74                head = next;
75            }
76            if (next != null) {
77                next.prev = prev;
78            }
79        }
80    }
81
82    /**
83     * Waits for all currently-enqueued references to be finalized.
84     */
85    public static void finalizeAllEnqueued(long timeout) throws InterruptedException {
86        // Alloate a new sentinel, this creates a FinalizerReference.
87        Sentinel sentinel;
88        // Keep looping until we safely enqueue our sentinel FinalizerReference.
89        // This is done to prevent races where the GC updates the pendingNext
90        // before we get the chance.
91        do {
92            sentinel = new Sentinel();
93        } while (!enqueueSentinelReference(sentinel));
94        sentinel.awaitFinalization(timeout);
95    }
96
97    private static boolean enqueueSentinelReference(Sentinel sentinel) {
98        synchronized (LIST_LOCK) {
99            // When a finalizable object is allocated, a FinalizerReference is added to the list.
100            // We search the list for that FinalizerReference (it should be at or near the head),
101            // and then put it on the queue so that it can be finalized.
102            for (FinalizerReference<?> r = head; r != null; r = r.next) {
103                if (r.referent == sentinel) {
104                    FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r;
105                    sentinelReference.referent = null;
106                    sentinelReference.zombie = sentinel;
107                    // Make a single element list, then enqueue the reference on the daemon unenqueued
108                    // list. This is required instead of enqueuing directly on the finalizer queue
109                    // since there could be recently freed objects in the unqueued list which are not
110                    // yet on the finalizer queue. This could cause the sentinel to run before the
111                    // objects are finalized. b/17381967
112                    // Make circular list if unenqueued goes through native so that we can prevent
113                    // races where the GC updates the pendingNext before we do. If it is non null, then
114                    // we update the pending next to make a circular list while holding a lock.
115                    // b/17462553
116                    if (!sentinelReference.makeCircularListIfUnenqueued()) {
117                        return false;
118                    }
119                    ReferenceQueue.add(sentinelReference);
120                    return true;
121                }
122            }
123        }
124        // We just created a finalizable object and still hold a reference to it.
125        // It must be on the list.
126        throw new AssertionError("newly-created live Sentinel not on list!");
127    }
128
129    private native boolean makeCircularListIfUnenqueued();
130
131    /**
132     * A marker object that we can immediately enqueue. When this object's
133     * finalize() method is called, we know all previously-enqueued finalizable
134     * references have been finalized.
135     */
136    private static class Sentinel {
137        boolean finalized = false;
138
139        @Override protected synchronized void finalize() throws Throwable {
140            if (finalized) {
141                throw new AssertionError();
142            }
143            finalized = true;
144            notifyAll();
145        }
146
147        synchronized void awaitFinalization(long timeout) throws InterruptedException {
148            final long startTime = System.nanoTime();
149            final long endTime = startTime + timeout;
150            while (!finalized) {
151                // 0 signifies no timeout.
152                if (timeout != 0) {
153                    final long currentTime = System.nanoTime();
154                    if (currentTime >= endTime) {
155                        break;
156                    } else {
157                        final long deltaTime = endTime - currentTime;
158                        wait(deltaTime / 1000000, (int)(deltaTime % 1000000));
159                    }
160                } else {
161                    wait();
162                }
163            }
164        }
165    }
166}
167