< 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 >