diff options
Diffstat (limited to 'luni/src/main/java/java/lang/ref/FinalizerReference.java')
-rw-r--r-- | luni/src/main/java/java/lang/ref/FinalizerReference.java | 30 |
1 files changed, 25 insertions, 5 deletions
diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java index 14eaae4..5416a80 100644 --- a/luni/src/main/java/java/lang/ref/FinalizerReference.java +++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java @@ -83,12 +83,18 @@ public final class FinalizerReference<T> extends Reference<T> { * Waits for all currently-enqueued references to be finalized. */ public static void finalizeAllEnqueued() throws InterruptedException { - Sentinel sentinel = new Sentinel(); - enqueueSentinelReference(sentinel); + // 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(); } - private static void enqueueSentinelReference(Sentinel sentinel) { + 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), @@ -98,8 +104,20 @@ public final class FinalizerReference<T> extends Reference<T> { FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r; sentinelReference.referent = null; sentinelReference.zombie = sentinel; - sentinelReference.enqueueInternal(); - return; + // 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; } } } @@ -108,6 +126,8 @@ public final class FinalizerReference<T> extends Reference<T> { throw new AssertionError("newly-created live Sentinel not on list!"); } + 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 |