/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2001-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.internal;
import java.util.logging.Level;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import org.apache.sis.util.Disposable;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.Classes;
/**
* Base class for thread that will process {@link Reference} enqueued in a {@link ReferenceQueue}.
* This thread invokes {@link Disposeable#dispose} on each enqueded {@linkplain Reference reference}.
* Every {@link Reference} implementations to be enqueued in {@link ReferenceQueueConsumer#queue}
* <strong>must</strong> implement the {@link Disposable} interface
*
* @param <T> The type of objects being referenced.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.13
*
* @since 3.00
* @module
*/
public class ReferenceQueueConsumer<T> extends DaemonThread {
/**
* The default thread.
*/
public static final ReferenceQueueConsumer<Object> DEFAULT;
static {
// Call to Thread.start() must be outside the constructor
// (Reference: Goetz et al.: "Java Concurrency in Practice").
DEFAULT = new ReferenceQueueConsumer<>("ReferenceQueueConsumer");
DEFAULT.start();
}
/**
* List of references collected by the garbage collector.
*/
public final ReferenceQueue<T> queue = new ReferenceQueue<>();
/**
* Constructs a new thread as a daemon. This thread will be sleeping most of the time.
* It will run only some few nanoseconds each time a new {@link Reference} is enqueded.
*
* @param name The thread name. This name appears in the debugger.
*/
protected ReferenceQueueConsumer(final String name) {
super(Threads.RESOURCE_DISPOSERS, name);
setPriority(MAX_PRIORITY - 2);
}
/**
* Invoked everytime a reference has been taken from the {@linkplain #queue}.
* The default implementation invokes {@link Disposable#dispose()} method.
* Subclasses can override this method if they want a different behavior.
*
* @param reference The reference (never {@code null}).
*/
protected void process(final Reference<? extends T> reference) {
/*
* If the reference does not implement the Disposeable interface, we want
* the ClassCastException to be logged in the "catch" block of the super
* class since it would be a programming error that we want to know about.
*/
((Disposable) reference).dispose();
}
/**
* Loop to be run during the virtual machine lifetime.
*/
@Override
public final void run() {
// The reference queue should never be null. However some strange cases (maybe caused
// by an anormal JVM state) have been reported on the mailing list. In such case, stop
// the daemon instead of writing 50 Mb of log messages.
Level level = Level.SEVERE;
ReferenceQueue<T> queue;
while ((queue = this.queue) != null) {
if (isKillRequested()) {
level = Level.INFO;
break;
}
final Reference<? extends T> ref;
try {
// Block until a reference is enqueded.
ref = queue.remove();
if (ref == null) {
/*
* Should never happen according Sun's Javadoc ("Removes the next reference
* object in this queue, blocking until one becomes available."). However a
* null reference seems to be returned during JVM shutdown on Linux. Wait a
* few seconds in order to give the JVM a chance to kill this daemon thread
* before the logging at the sever level, and stop the loop. We do not try
* to resume the loop since something is apparently going wrong and we want
* the user to be notified. See GEOT-1138.
*/
sleep(15 * 1000L);
break;
}
} catch (InterruptedException exception) {
// Somebody doesn't want to lets us sleep... Go back to work.
continue;
}
try {
process(ref);
} catch (Throwable exception) {
Logging.unexpectedException(null, getClass(), "run", exception);
}
}
Logging.getLogger("org.geotoolkit.internal").log(level, "{0} daemon stopped.", Classes.getShortClassName(this));
}
}