< prev index next >

src/java.base/share/classes/jdk/internal/ref/CleanerImpl.java

Print this page

        

*** 23,55 **** * questions. */ package jdk.internal.ref; 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.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; ! import java.util.function.Function; ! ! import jdk.internal.misc.InnocuousThread; /** ! * CleanerImpl manages a set of object references and corresponding cleaning actions. ! * CleanerImpl provides the functionality of {@link java.lang.ref.Cleaner}. */ ! public final class CleanerImpl implements Runnable { /** ! * An object to access the CleanerImpl from a Cleaner; set by Cleaner init. */ ! private static Function<Cleaner, CleanerImpl> cleanerImplAccess = null; /** * Heads of a CleanableList for each reference type. */ final PhantomCleanable<?> phantomCleanableList; final WeakCleanable<?> weakCleanableList; --- 23,159 ---- * questions. */ package jdk.internal.ref; + import jdk.internal.misc.InnocuousThread; + import jdk.internal.misc.JavaLangRefAccess; + import jdk.internal.misc.SharedSecrets; + import jdk.internal.vm.annotation.ReservedStackAccess; + import java.lang.ref.Cleaner; 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.concurrent.locks.StampedLock; ! import java.util.function.BooleanSupplier; ! import java.util.function.Supplier; /** ! * CleanerImpl is the implementation of {@link Cleaner}. */ ! 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); ! } /** ! * CleanerImpl.ExtendedImpl is the implementation of {@link ExtendedCleaner}. */ ! static class ExtendedImpl extends CleanerImpl implements ExtendedCleaner { ! ! ExtendedImpl(ThreadFactory threadFactory) { ! super(threadFactory); ! } ! ! // A fair lock for threads that retry actions to queue after ! // 1st optimistic try fails so that only a single thread at a time is ! // retrying actions while helping the Cleaner execute Cleanable(s) ! // and trigger new Reference discovery before finally giving up. ! private final StampedLock helpingLock = new StampedLock(); ! ! @Override ! public <T> T retryWhileHelpingClean(Supplier<T> retriableAction) { ! T result; ! // 1st optimistic try - allow concurrent execution of actions ! // until helping is necessary ! long stamp = helpingLock.tryReadLock(); ! if (stamp != 0) try { ! if ((result = retriableAction.get()) != null) { ! return result; ! } ! } finally { ! helpingLock.unlockRead(stamp); ! } ! ! // retrials with helping are exclusive ! stamp = helpingLock.writeLock(); ! try { ! // retry action while executing enqueued Cleanable(s) until the ! // queue drains out ! do { ! if ((result = retriableAction.get()) != null) { ! return result; ! } ! } while (task.cleanNextEnqueued()); ! ! // the queue is now empty but there may have been a long time ! // since last Reference discovery has been performed. ! 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 some freshly discovered ! // Cleanable(s) so retry operation while executing enqueued Cleanable(s) ! // until the queue drains out ! do { ! if ((result = retriableAction.get()) != null) { ! return result; ! } ! } while (task.cleanNextEnqueued()); ! ! // give up finally ! return null; ! } 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,92 **** // 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 */ ! static CleanerImpl getCleanerImpl(Cleaner cleaner) { ! return cleanerImplAccess.apply(cleaner); ! } ! ! /** ! * Constructor for CleanerImpl. ! */ ! public CleanerImpl() { queue = new ReferenceQueue<>(); phantomCleanableList = new PhantomCleanableRef(); weakCleanableList = new WeakCleanableRef(); softCleanableList = new SoftCleanableRef(); } --- 162,174 ---- // The ReferenceQueue of pending cleaning actions final ReferenceQueue<Object> queue; /** ! * Constructor for Task. */ ! Task() { queue = new ReferenceQueue<>(); phantomCleanableList = new PhantomCleanableRef(); weakCleanableList = new WeakCleanableRef(); softCleanableList = new SoftCleanableRef(); }
*** 96,107 **** * 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) { 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); --- 178,189 ---- * 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 */ ! 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,164 **** --- 237,268 ---- } } } /** + * 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. + */ + @ReservedStackAccess + 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 >