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 17 package java.lang.ref; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import dalvik.annotation.optimization.FastNative; 21 22 /** 23 * @hide 24 */ 25 public final class FinalizerReference<T> extends Reference<T> { 26 // This queue contains those objects eligible for finalization. 27 @UnsupportedAppUsage 28 public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); 29 30 // Guards the list (not the queue). 31 private static final Object LIST_LOCK = new Object(); 32 33 // This list contains a FinalizerReference for every finalizable object in the heap. 34 // Objects in this list may or may not be eligible for finalization yet. 35 @UnsupportedAppUsage 36 private static FinalizerReference<?> head = null; 37 38 // The links used to construct the list. 39 private FinalizerReference<?> prev; 40 @UnsupportedAppUsage 41 private FinalizerReference<?> next; 42 43 // When the GC wants something finalized, it moves it from the 'referent' field to 44 // the 'zombie' field instead. 45 private T zombie; 46 FinalizerReference(T r, ReferenceQueue<? super T> q)47 public FinalizerReference(T r, ReferenceQueue<? super T> q) { 48 super(r, q); 49 } 50 get()51 @Override public T get() { 52 return zombie; 53 } 54 clear()55 @Override public void clear() { 56 zombie = null; 57 } 58 59 @UnsupportedAppUsage add(Object referent)60 public static void add(Object referent) { 61 FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue); 62 synchronized (LIST_LOCK) { 63 reference.prev = null; 64 reference.next = head; 65 if (head != null) { 66 head.prev = reference; 67 } 68 head = reference; 69 } 70 } 71 72 @UnsupportedAppUsage remove(FinalizerReference<?> reference)73 public static void remove(FinalizerReference<?> reference) { 74 synchronized (LIST_LOCK) { 75 FinalizerReference<?> next = reference.next; 76 FinalizerReference<?> prev = reference.prev; 77 reference.next = null; 78 reference.prev = null; 79 if (prev != null) { 80 prev.next = next; 81 } else { 82 head = next; 83 } 84 if (next != null) { 85 next.prev = prev; 86 } 87 } 88 } 89 90 /** 91 * Waits for all currently-enqueued references to be finalized. 92 */ finalizeAllEnqueued(long timeout)93 public static void finalizeAllEnqueued(long timeout) throws InterruptedException { 94 // Alloate a new sentinel, this creates a FinalizerReference. 95 Sentinel sentinel; 96 // Keep looping until we safely enqueue our sentinel FinalizerReference. 97 // This is done to prevent races where the GC updates the pendingNext 98 // before we get the chance. 99 do { 100 sentinel = new Sentinel(); 101 } while (!enqueueSentinelReference(sentinel)); 102 sentinel.awaitFinalization(timeout); 103 } 104 enqueueSentinelReference(Sentinel sentinel)105 private static boolean enqueueSentinelReference(Sentinel sentinel) { 106 synchronized (LIST_LOCK) { 107 // When a finalizable object is allocated, a FinalizerReference is added to the list. 108 // We search the list for that FinalizerReference (it should be at or near the head), 109 // and then put it on the queue so that it can be finalized. 110 for (FinalizerReference<?> r = head; r != null; r = r.next) { 111 // Use getReferent() instead of directly accessing the referent field not to race 112 // with GC reference processing. Can't use get() either because it's overridden to 113 // return the zombie. 114 if (r.getReferent() == sentinel) { 115 FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r; 116 sentinelReference.clearReferent(); 117 sentinelReference.zombie = sentinel; 118 // Make a single element list, then enqueue the reference on the daemon unenqueued 119 // list. This is required instead of enqueuing directly on the finalizer queue 120 // since there could be recently freed objects in the unqueued list which are not 121 // yet on the finalizer queue. This could cause the sentinel to run before the 122 // objects are finalized. b/17381967 123 // Make circular list if unenqueued goes through native so that we can prevent 124 // races where the GC updates the pendingNext before we do. If it is non null, then 125 // we update the pending next to make a circular list while holding a lock. 126 // b/17462553 127 if (!sentinelReference.makeCircularListIfUnenqueued()) { 128 return false; 129 } 130 ReferenceQueue.add(sentinelReference); 131 return true; 132 } 133 } 134 } 135 // We just created a finalizable object and still hold a reference to it. 136 // It must be on the list. 137 throw new AssertionError("newly-created live Sentinel not on list!"); 138 } 139 140 @FastNative getReferent()141 private final native T getReferent(); 142 @FastNative makeCircularListIfUnenqueued()143 private native boolean makeCircularListIfUnenqueued(); 144 145 /** 146 * A marker object that we can immediately enqueue. When this object's 147 * finalize() method is called, we know all previously-enqueued finalizable 148 * references have been finalized. 149 */ 150 private static class Sentinel { 151 boolean finalized = false; 152 finalize()153 @Override protected synchronized void finalize() throws Throwable { 154 if (finalized) { 155 throw new AssertionError(); 156 } 157 finalized = true; 158 notifyAll(); 159 } 160 awaitFinalization(long timeout)161 synchronized void awaitFinalization(long timeout) throws InterruptedException { 162 final long startTime = System.nanoTime(); 163 final long endTime = startTime + timeout; 164 while (!finalized) { 165 // 0 signifies no timeout. 166 if (timeout != 0) { 167 final long currentTime = System.nanoTime(); 168 if (currentTime >= endTime) { 169 break; 170 } else { 171 final long deltaTime = endTime - currentTime; 172 wait(deltaTime / 1000000, (int)(deltaTime % 1000000)); 173 } 174 } else { 175 wait(); 176 } 177 } 178 } 179 } 180 } 181