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