diff --git a/src/com/sun/jna/internal/Cleaner.java b/src/com/sun/jna/internal/Cleaner.java index a2095937f..b5f5210c4 100644 --- a/src/com/sun/jna/internal/Cleaner.java +++ b/src/com/sun/jna/internal/Cleaner.java @@ -27,6 +27,7 @@ import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -35,84 +36,80 @@ * objects. It replaces the {@code Object#finalize} based resource deallocation * that is deprecated for removal from the JDK. * - *

This class is intented to be used only be JNA itself.

+ *

This class is intended to be used only be JNA itself.

*/ public class Cleaner { private static final Cleaner INSTANCE = new Cleaner(); + private static final Logger logger = Logger.getLogger(Cleaner.class.getName()); + private static final long CLEANER_LINGER_TIME = TimeUnit.SECONDS.toMillis(30); public static Cleaner getCleaner() { return INSTANCE; } private final ReferenceQueue referenceQueue; - private Thread cleanerThread; + private boolean cleanerRunning; private CleanerRef firstCleanable; private Cleaner() { referenceQueue = new ReferenceQueue<>(); } - public synchronized Cleanable register(Object obj, Runnable cleanupTask) { - // The important side effect is the PhantomReference, that is yielded - // after the referent is GCed - return add(new CleanerRef(this, obj, referenceQueue, cleanupTask)); - } + public synchronized Cleanable register(final Object obj, final Runnable cleanupTask) { + // The important side effect is the PhantomReference, that is yielded after the referent is GCed + final CleanerRef ref = new CleanerRef(obj, referenceQueue, cleanupTask); - private synchronized CleanerRef add(CleanerRef ref) { - synchronized (referenceQueue) { - if (firstCleanable == null) { - firstCleanable = ref; - } else { - ref.setNext(firstCleanable); - firstCleanable.setPrevious(ref); - firstCleanable = ref; - } - if (cleanerThread == null) { - Logger.getLogger(Cleaner.class.getName()).log(Level.FINE, "Starting CleanerThread"); - cleanerThread = new CleanerThread(); - cleanerThread.start(); - } - return ref; + if (firstCleanable != null) { + ref.setNext(firstCleanable); + firstCleanable.setPrevious(ref); + } + firstCleanable = ref; + + if (!cleanerRunning) { + logger.log(Level.FINE, "Starting CleanerThread"); + Thread cleanerThread = new CleanerThread(); + cleanerThread.start(); + cleanerRunning = true; } + + return ref; } - private synchronized boolean remove(CleanerRef ref) { - synchronized (referenceQueue) { - boolean inChain = false; - if (ref == firstCleanable) { - firstCleanable = ref.getNext(); - inChain = true; - } - if (ref.getPrevious() != null) { - ref.getPrevious().setNext(ref.getNext()); - } - if (ref.getNext() != null) { - ref.getNext().setPrevious(ref.getPrevious()); - } - if (ref.getPrevious() != null || ref.getNext() != null) { - inChain = true; - } - ref.setNext(null); - ref.setPrevious(null); - return inChain; + private synchronized boolean remove(final CleanerRef ref) { + final CleanerRef prev = ref.getPrevious(); + final CleanerRef next = ref.getNext(); + boolean inChain = false; + + if (ref == firstCleanable) { + firstCleanable = next; + inChain = true; + } + if (prev != null) { + prev.setNext(next); + inChain = true; } + if (next != null) { + next.setPrevious(prev); + inChain = true; + } + return inChain; } private static class CleanerRef extends PhantomReference implements Cleanable { - private final Cleaner cleaner; private final Runnable cleanupTask; private CleanerRef previous; private CleanerRef next; - public CleanerRef(Cleaner cleaner, Object referent, ReferenceQueue q, Runnable cleanupTask) { - super(referent, q); - this.cleaner = cleaner; + public CleanerRef(final Object referent, final ReferenceQueue queue, final Runnable cleanupTask) { + super(referent, queue); this.cleanupTask = cleanupTask; } @Override public void clean() { - if(cleaner.remove(this)) { + if (INSTANCE.remove(this)) { + previous = null; + next = null; cleanupTask.run(); } } @@ -121,7 +118,7 @@ CleanerRef getPrevious() { return previous; } - void setPrevious(CleanerRef previous) { + void setPrevious(final CleanerRef previous) { this.previous = previous; } @@ -129,19 +126,17 @@ CleanerRef getNext() { return next; } - void setNext(CleanerRef next) { + void setNext(final CleanerRef next) { this.next = next; } } - public static interface Cleanable { - public void clean(); + public interface Cleanable { + void clean(); } private class CleanerThread extends Thread { - private static final long CLEANER_LINGER_TIME = 30000; - public CleanerThread() { super("JNA Cleaner"); setDaemon(true); @@ -151,20 +146,19 @@ public CleanerThread() { public void run() { while (true) { try { - Reference ref = referenceQueue.remove(CLEANER_LINGER_TIME); + Reference ref = referenceQueue.remove(CLEANER_LINGER_TIME); if (ref instanceof CleanerRef) { ((CleanerRef) ref).clean(); } else if (ref == null) { - synchronized (referenceQueue) { - Logger logger = Logger.getLogger(Cleaner.class.getName()); + synchronized (INSTANCE) { if (firstCleanable == null) { - cleanerThread = null; logger.log(Level.FINE, "Shutting down CleanerThread"); + cleanerRunning = false; break; } else if (logger.isLoggable(Level.FINER)) { StringBuilder registeredCleaners = new StringBuilder(); - for(CleanerRef cleanerRef = firstCleanable; cleanerRef != null; cleanerRef = cleanerRef.next) { - if(registeredCleaners.length() != 0) { + for (CleanerRef cleanerRef = firstCleanable; cleanerRef != null; cleanerRef = cleanerRef.next) { + if (registeredCleaners.length() != 0) { registeredCleaners.append(", "); } registeredCleaners.append(cleanerRef.cleanupTask.toString()); @@ -178,6 +172,9 @@ public void run() { // our reference queue, well, there is no way to separate // the two cases. // https://groups.google.com/g/jna-users/c/j0fw96PlOpM/m/vbwNIb2pBQAJ + synchronized (INSTANCE) { + cleanerRunning = false; + } break; } catch (Exception ex) { Logger.getLogger(Cleaner.class.getName()).log(Level.SEVERE, null, ex);