package java.lang.ref;
import java.util.Objects;
/**
* A {@link PhantomReference} based {@link Cleaner}.
*
* PhantomCleaner(s) are a lightweight and more robust alternative to finalization.
* They are lightweight because they are not created by the VM and thus do not
* require a JNI upcall to be created. They are more robust because they are
* phantom references, the weakest type of reference object, thereby avoiding the
* nasty ordering problems inherent to finalization.
*
* Some time after the GC detects that PhantomCleaner's referent has
* become phantom-reachable, one of the finalizing thread(s) will
* {@link #clean() invoke} the cleaner.
* PhantomCleaner(s) may also be {@link #clean() invoked} directly. The 1st
* invocation will {@link #clear()} the PhantomCleaner and invoke the
* {@link #doClean() clean-up action}. Subsequent invocations are ignored.
*
* @since 1.9
*/
public abstract class PhantomCleaner extends PhantomReference implements Cleaner {
/**
* Creates and returns a PhantomCleaner implementation that takes a
* {@code Runnable thunk} to be run as a clean-up action.
* Some time after the {@code referent} becomes phantom-reachable, one of
* the finalizing threads runs the {@code thunk}.
*
* @param referent the referent object to be tracked
* @param thunk The cleanup code to be run when the cleaner is invoked. The
* cleanup code is run from one of the finalizing thread(s)
* that are also used to run other cleaners and finalization or
* from user code that invokes the cleaner directly.
* @return The new PhantomCleaner
* @throws NullPointerException if {@code referent} or {@code thunk} is null.
*/
public static PhantomCleaner create(Object referent, Runnable thunk) {
Objects.requireNonNull(thunk);
return new PhantomCleaner(referent) {
@Override
protected void doClean() {
thunk.run();
}
};
}
/**
* A constructor taking a referent to track.
*
* @param referent the referent to track
* @throws NullPointerException if {@code referent} is null
*/
public PhantomCleaner(Object referent) {
super(Objects.requireNonNull(referent), null);
link();
}
/**
* Invoked at most once after the referent of this
* {@code PhantomCleaner} is found phantom-reachable or when the user
* explicitly invokes {@link #clean()}. Subclasses should implement this
* method and place clean-up actions into it.
*/
protected abstract void doClean();
/**
* Runs this PhantomCleaner, if it has not been run before. This method may be
* invoked by one of the finalizing thread(s) or manually by a client
* thread. Only the 1st invocation will {@link #clear()} this PhantomCleaner
* and invoke {@link #doClean() cleanup-code}. Any unchecked
* exception thrown from the {@link #doClean() cleanup-code} will be propagated.
* When such exception happens as a result of running this method automatically
* by one of the finalizing thread(s), it is ignored.
*/
@Override
public final void clean() {
if (delete()) {
super.clear();
unlink();
doClean();
}
}
@Override
public final void clear() {
if (delete()) {
super.clear();
unlink();
}
}
// Methods and state that enable PhantomCleaner to be an element of DLList
@SuppressWarnings("unused") // assigned through Unsafe
private volatile Reference> prevDll, nextDll;
@SuppressWarnings("unused") // assigned through Unsafe
private volatile int deletedDll;
private boolean delete() {
return deletedDll == 0 && UNSAFE.compareAndSwapInt(this, DELETED_DLL, 0, 1);
}
boolean isDeletedDll() {
return deletedDll == 1;
}
Reference> getPrevDll() {
return prevDll;
}
void lazySetPrevDll(Reference> val) {
UNSAFE.putOrderedObject(this, PREV_DLL, val);
}
boolean casPrevDll(Reference> cmp, Reference> val) {
return UNSAFE.compareAndSwapObject(this, PREV_DLL, cmp, val);
}
Reference> getNextDll() {
return nextDll;
}
void lazySetNextDll(Reference> val) {
UNSAFE.putOrderedObject(this, NEXT_DLL, val);
}
boolean casNextDll(Reference> cmp, Reference> val) {
return UNSAFE.compareAndSwapObject(this, NEXT_DLL, cmp, val);
}
// Unsafe machinery
private static final sun.misc.Unsafe UNSAFE;
private static final long PREV_DLL;
private static final long NEXT_DLL;
private static final long DELETED_DLL;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class pcc = PhantomCleaner.class;
PREV_DLL = UNSAFE.objectFieldOffset(pcc.getDeclaredField("prevDll"));
NEXT_DLL = UNSAFE.objectFieldOffset(pcc.getDeclaredField("nextDll"));
DELETED_DLL = UNSAFE.objectFieldOffset(pcc.getDeclaredField("deletedDll"));
} catch (Exception e) {
throw new Error(e);
}
}
}