< prev index next >
src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java
Print this page
@@ -23,33 +23,131 @@
* questions.
*/
package jdk.internal.ref;
+import jdk.internal.misc.InnocuousThread;
+import jdk.internal.misc.JavaLangRefAccess;
+import jdk.internal.misc.SharedSecrets;
+
import java.lang.ref.Cleaner;
-import java.lang.ref.Cleaner.Cleanable;
import java.lang.ref.ReferenceQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
-
-import jdk.internal.misc.InnocuousThread;
+import java.util.concurrent.locks.StampedLock;
+import java.util.function.BooleanSupplier;
/**
- * CleanerImpl manages a set of object references and corresponding cleaning actions.
- * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}.
+ * CleanerImpl is the implementation of {@link Cleaner}.
*/
-public final class CleanerImpl implements Runnable {
+public class CleanerImpl implements Cleaner {
+
+ final Task task;
+
+ public CleanerImpl(ThreadFactory threadFactory) {
+ task = new Task();
+ task.start(this, threadFactory);
+ }
+
+ @Override
+ public Cleanable register(Object obj, Runnable action) {
+ Objects.requireNonNull(obj, "obj");
+ Objects.requireNonNull(action, "action");
+ return new CleanerImpl.PhantomCleanableRef(obj, this, action);
+ }
/**
- * An object to access the CleanerImpl from a Cleaner; set by Cleaner init.
+ * CleanerImpl.ExtendedImpl is the implementation of {@link ExtendedCleaner}.
*/
- private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null;
+ static class ExtendedImpl extends CleanerImpl implements ExtendedCleaner {
+
+ ExtendedImpl(ThreadFactory threadFactory) {
+ super(threadFactory);
+ }
+
+ // A fair lock for threads that retry operations to queue after
+ // 1st optimistic try fails so that only a single thread at a time is
+ // retrying operations while helping the Cleaner execute Cleanable(s)
+ // and trigger new Reference discovery before finally giving up.
+ private final StampedLock helpingLock = new StampedLock();
+
+ public boolean retryWhileHelpingClean(BooleanSupplier retriableOperation) {
+ // 1st optimistic try - allow concurrent execution of operations
+ // until helping is necessary
+ long stamp = helpingLock.tryReadLock();
+ if (stamp != 0) try {
+ if (retriableOperation.getAsBoolean()) {
+ return true;
+ }
+ } finally {
+ helpingLock.unlockRead(stamp);
+ }
+
+ // retrials with helping is exclusive
+ stamp = helpingLock.writeLock();
+ try {
+ // retry operation while executing enqueued Cleanable(s) until the
+ // queue drains out
+ do {
+ if (retriableOperation.getAsBoolean()) {
+ return true;
+ }
+ } while (task.cleanNextEnqueued());
+
+ JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
+ // trigger Reference(s) discovery
+ int discoveryPhase = jlra.discoverReferences();
+ // wait for newly discovered Reference(s) to be enqueued
+ boolean interrupted = false;
+ try {
+ while (true) {
+ try {
+ jlra.awaitReferencesEnqueued(discoveryPhase);
+ break;
+ } catch (InterruptedException e) {
+ // ignore interrupts but don't swallow them
+ interrupted = true;
+ }
+ }
+ } finally {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ // the queue is now hopefully filled with new pending Cleanable(s)
+ // so retry operation while executing enqueued Cleanable(s) until the
+ // queue drains out
+ do {
+ if (retriableOperation.getAsBoolean()) {
+ return true;
+ }
+ } while (task.cleanNextEnqueued());
+
+ // give up finally
+ return false;
+ } finally {
+ helpingLock.unlockWrite(stamp);
+ }
+ }
+ }
+
+ // package-private access to Task's state
+ PhantomCleanable<?> phantomCleanableList() { return task.phantomCleanableList; }
+ WeakCleanable<?> weakCleanableList() { return task.weakCleanableList; }
+ SoftCleanable<?> softCleanableList() { return task.softCleanableList; }
+ ReferenceQueue<Object> queue() { return task.queue; }
/**
+ * CleanerImpl.Task manages a set of object references and corresponding
+ * cleaning actions and executes them after they are enqueued.
+ */
+ private static final class Task implements Runnable {
+ /**
* Heads of a CleanableList for each reference type.
*/
final PhantomCleanable<?> phantomCleanableList;
final WeakCleanable<?> weakCleanableList;
@@ -58,35 +156,13 @@
// The ReferenceQueue of pending cleaning actions
final ReferenceQueue<Object> queue;
/**
- * Called by Cleaner static initialization to provide the function
- * to map from Cleaner to CleanerImpl.
- * @param access a function to map from Cleaner to CleanerImpl
- */
- public static void setCleanerImplAccess(Function<Cleaner, CleanerImpl> access) {
- if (cleanerImplAccess == null) {
- cleanerImplAccess = access;
- } else {
- throw new InternalError("cleanerImplAccess");
- }
- }
-
- /**
- * Called to get the CleanerImpl for a Cleaner.
- * @param cleaner the cleaner
- * @return the corresponding CleanerImpl
+ * Constructor for Task.
*/
- static CleanerImpl getCleanerImpl(Cleaner cleaner) {
- return cleanerImplAccess.apply(cleaner);
- }
-
- /**
- * Constructor for CleanerImpl.
- */
- public CleanerImpl() {
+ Task() {
queue = new ReferenceQueue<>();
phantomCleanableList = new PhantomCleanableRef();
weakCleanableList = new WeakCleanableRef();
softCleanableList = new SoftCleanableRef();
}
@@ -96,12 +172,12 @@
* Ensure this is the CleanerImpl for the Cleaner.
* When started waits for Cleanables to be queued.
* @param cleaner the cleaner
* @param threadFactory the thread factory
*/
- public void start(Cleaner cleaner, ThreadFactory threadFactory) {
- if (getCleanerImpl(cleaner) != this) {
+ void start(CleanerImpl cleaner, ThreadFactory threadFactory) {
+ if (cleaner.task != this) {
throw new AssertionError("wrong cleaner");
}
// schedule a nop cleaning action for the cleaner, so the associated thread
// will continue to run at least until the cleaner is reclaimable.
new CleanerCleanable(cleaner);
@@ -155,10 +231,31 @@
}
}
}
/**
+ * Processes next Cleanable that has been waiting in the queue.
+ *
+ * @return {@code true} if a Cleanable was found in the queue and
+ * was processed or {@code false} if the queue was empty.
+ */
+ boolean cleanNextEnqueued() {
+ Cleanable ref = (Cleanable) queue.poll();
+ if (ref != null) {
+ try {
+ ref.clean();
+ } catch (Throwable t) {
+ // ignore exceptions from the cleanup action
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
* Perform cleaning on an unreachable PhantomReference.
*/
public static final class PhantomCleanableRef extends PhantomCleanable<Object> {
private final Runnable action;
< prev index next >