blob: a71044ca7b251005b722d6b2f447bdd2309ad9a9 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.lang.ref;
import android.compat.annotation.UnsupportedAppUsage;
import dalvik.annotation.optimization.FastNative;
/**
* @hide
*/
public final class FinalizerReference<T> extends Reference<T> {
// This queue contains those objects eligible for finalization.
@UnsupportedAppUsage
public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
// Guards the list (not the queue).
private static final Object LIST_LOCK = new Object();
// This list contains a FinalizerReference for every finalizable object in the heap.
// Objects in this list may or may not be eligible for finalization yet.
@UnsupportedAppUsage
private static FinalizerReference<?> head = null;
// The links used to construct the list.
private FinalizerReference<?> prev;
@UnsupportedAppUsage
private FinalizerReference<?> next;
// When the GC wants something finalized, it moves it from the 'referent' field to
// the 'zombie' field instead.
private T zombie;
public FinalizerReference(T r, ReferenceQueue<? super T> q) {
super(r, q);
}
@Override public T get() {
return zombie;
}
@Override public void clear() {
zombie = null;
}
@UnsupportedAppUsage
public static void add(Object referent) {
FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue);
synchronized (LIST_LOCK) {
reference.prev = null;
reference.next = head;
if (head != null) {
head.prev = reference;
}
head = reference;
}
}
@UnsupportedAppUsage
public static void remove(FinalizerReference<?> reference) {
synchronized (LIST_LOCK) {
FinalizerReference<?> next = reference.next;
FinalizerReference<?> prev = reference.prev;
reference.next = null;
reference.prev = null;
if (prev != null) {
prev.next = next;
} else {
head = next;
}
if (next != null) {
next.prev = prev;
}
}
}
/**
* Waits for all currently-enqueued references to be finalized.
*/
public static void finalizeAllEnqueued(long timeout) throws InterruptedException {
// Alloate a new sentinel, this creates a FinalizerReference.
Sentinel sentinel;
// Keep looping until we safely enqueue our sentinel FinalizerReference.
// This is done to prevent races where the GC updates the pendingNext
// before we get the chance.
do {
sentinel = new Sentinel();
} while (!enqueueSentinelReference(sentinel));
sentinel.awaitFinalization(timeout);
}
private static boolean enqueueSentinelReference(Sentinel sentinel) {
synchronized (LIST_LOCK) {
// When a finalizable object is allocated, a FinalizerReference is added to the list.
// We search the list for that FinalizerReference (it should be at or near the head),
// and then put it on the queue so that it can be finalized.
for (FinalizerReference<?> r = head; r != null; r = r.next) {
// Use getReferent() instead of directly accessing the referent field not to race
// with GC reference processing. Can't use get() either because it's overridden to
// return the zombie.
if (r.getReferent() == sentinel) {
FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r;
sentinelReference.clearReferent();
sentinelReference.zombie = sentinel;
// Make a single element list, then enqueue the reference on the daemon unenqueued
// list. This is required instead of enqueuing directly on the finalizer queue
// since there could be recently freed objects in the unqueued list which are not
// yet on the finalizer queue. This could cause the sentinel to run before the
// objects are finalized. b/17381967
// Make circular list if unenqueued goes through native so that we can prevent
// races where the GC updates the pendingNext before we do. If it is non null, then
// we update the pending next to make a circular list while holding a lock.
// b/17462553
if (!sentinelReference.makeCircularListIfUnenqueued()) {
return false;
}
ReferenceQueue.add(sentinelReference);
return true;
}
}
}
// We just created a finalizable object and still hold a reference to it.
// It must be on the list.
throw new AssertionError("newly-created live Sentinel not on list!");
}
@FastNative
private final native T getReferent();
@FastNative
private native boolean makeCircularListIfUnenqueued();
/**
* A marker object that we can immediately enqueue. When this object's
* finalize() method is called, we know all previously-enqueued finalizable
* references have been finalized.
*/
private static class Sentinel {
boolean finalized = false;
@Override protected synchronized void finalize() throws Throwable {
if (finalized) {
throw new AssertionError();
}
finalized = true;
notifyAll();
}
synchronized void awaitFinalization(long timeout) throws InterruptedException {
final long startTime = System.nanoTime();
final long endTime = startTime + timeout; // May wrap.
while (!finalized) {
// 0 signifies no timeout.
if (timeout != 0) {
final long deltaTime = endTime - System.nanoTime();
if (deltaTime <= 0) {
break;
} else {
wait(deltaTime / 1_000_000, (int)(deltaTime % 1_000_000));
}
} else {
wait();
}
}
}
}
}