summaryrefslogtreecommitdiffstats
path: root/luni/src/main/java/java/lang/ref/FinalizerReference.java
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/main/java/java/lang/ref/FinalizerReference.java')
-rw-r--r--luni/src/main/java/java/lang/ref/FinalizerReference.java30
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