You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2012/10/03 10:40:55 UTC
svn commit: r1393350 - in
/sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util:
DaemonThread.java ReferenceQueueConsumer.java Threads.java
Author: desruisseaux
Date: Wed Oct 3 08:40:55 2012
New Revision: 1393350
URL: http://svn.apache.org/viewvc?rev=1393350&view=rev
Log:
Maintains a chained list of DaemonThreads to terminate when a OSGi bundle is desactivated.
Modified:
sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/DaemonThread.java
sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/ReferenceQueueConsumer.java
sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/Threads.java
Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/DaemonThread.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/DaemonThread.java?rev=1393350&r1=1393349&r2=1393350&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/DaemonThread.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/DaemonThread.java Wed Oct 3 08:40:55 2012
@@ -16,15 +16,18 @@
*/
package org.apache.sis.internal.util;
+import java.util.List;
+import java.util.ArrayList;
+
/**
- * Base class for all daemon threads in the SIS library. This class provides a
- * {@link #isKillRequested()} flag which shall be tested by the daemon threads.
- * It is okay to test this flag only when catching {@link InterruptedException},
- * as below:
+ * Base class for all daemon threads in the SIS library. All {@code DaemonThread} instances are
+ * expected to run for the whole JVM lifetime (they are <strong>not</strong> executor threads).
+ * This class provides a {@link #isKillRequested()} flag which shall be tested by the subclasses.
+ * It is okay to test this flag only when catching {@link InterruptedException}, as below:
*
* {@preformat java
- * while (someCondition) {
+ * while (true) {
* try {
* someObject.wait();
* } catch (InterruptedException e) {
@@ -40,57 +43,118 @@ package org.apache.sis.internal.util;
* @version 0.3
* @module
*/
-public class DaemonThread extends Thread {
+abstract class DaemonThread extends Thread {
/**
- * Set to {@code true} when the {@link #kill()} method has been invoked.
+ * The previous element in a chain of {@code DaemonThread}s. We maintain a linked list of
+ * {@code DaemonThread} to be killed when {@link #killAll(DaemonThread)} will be invoked.
+ * We do not rely on the thread listed by the {@link Threads#RESOURCE_DISPOSERS} group
+ * because in an OSGi context, we need to handle separately the threads created by each
+ * SIS module.
+ */
+ private final DaemonThread previous;
+
+ /**
+ * Set to {@code true} when a kill is requested.
*/
private volatile boolean killRequested;
/**
* Creates a new daemon thread. This constructor sets the daemon flag to {@code true}.
+ * <p>
+ * We need to maintain a list of daemon threads created by each SIS module in order to
+ * kill them at shutdown time (not strictly necessary for pure JSEE applications, but
+ * required in OSGi environment). Each module using {@code DaemonThread} shall maintain
+ * its <strong>own</strong> list (don't use the list of another module), like below:
+ *
+ * {@preformat java
+ * class MyInternalClass {
+ * static DaemonThread lastCreatedDaemon;
+ * }
+ *
+ * class AnOtherClass {
+ * private static final MyDaemonThread;
+ * static {
+ * synchronized (MyInternalClass.class) {
+ * MyInternalClass.lastCreatedDaemon = myDaemonThread = new MyDaemonThread(
+ * Threads.RESOURCE_DISPOSERS, "MyThread", MyInternalClass.lastCreatedDaemon);
+ * }
+ * }
+ * }
+ *
+ * See {@link ReferenceQueueConsumer} for a real example.
*
* @param group The thread group.
* @param name The thread name.
+ * @param lastCreatedDaemon The previous element in a chain of {@code DaemonThread}s,
+ * or {@code null}. Each SIS module shall maintain its own chain, if any.
*/
- protected DaemonThread(final ThreadGroup group, final String name) {
+ protected DaemonThread(final ThreadGroup group, final String name, final DaemonThread lastCreatedDaemon) {
super(group, name);
+ previous = lastCreatedDaemon;
setDaemon(true);
}
/**
- * Returns {@code true} if {@link #kill()} has been invoked.
+ * Must be overridden by subclass for performing the actual work.
+ */
+ @Override
+ public abstract void run();
+
+ /**
+ * Returns {@code true} if this daemon thread shall terminate.
+ * This happen at shutdown time.
*
- * @return {@code true} if {@link #kill()} has been invoked.
+ * @return {@code true} if this daemon thread shall terminate.
*/
protected final boolean isKillRequested() {
return killRequested;
}
/**
- * Kills all the given threads (ignoring null arguments),
+ * Sends a kill signal to all threads in the chain starting by the given thread,
* and waits for the threads to die before to return.
+ * <p>
+ * <strong>This method is for internal use by Apache SIS shutdown hooks only.</strong>
+ * Users should never invoke this method explicitely.
*
- * @param stopWaitingAt Value of {@link System#nanoTime()} when to stop waiting.
- * This is used for preventing the shutdown to block an indefinite amount of time.
- * @param threads The threads to kill. Null arguments are silently ignored.
+ * @param first The first thread in the chain of threads to kill.
+ * @param stopWaitingAt A {@link System#nanoTime()} value telling when to stop waiting.
+ * This is used for preventing shutdown process to block an indefinite amount of time.
* @throws InterruptedException If an other thread invoked {@link #interrupt()} while
* we were waiting for the daemon threads to die.
+ *
+ * @see Threads#shutdown(long)
*/
- static void kill(final long stopWaitingAt, final DaemonThread... threads)
- throws InterruptedException
- {
- for (final DaemonThread thread : threads) {
- if (thread != null) {
- thread.killRequested = true;
- thread.interrupt();
- }
+ static void killAll(final DaemonThread first, final long stopWaitingAt) throws InterruptedException {
+ for (DaemonThread thread=first; thread!=null; thread=thread.previous) {
+ thread.killRequested = true;
+ thread.interrupt();
}
- for (final DaemonThread thread : threads) {
- if (thread != null) {
- final long delay = stopWaitingAt - System.nanoTime();
- if (delay <= 0) break;
- thread.join(delay);
+ for (DaemonThread thread=first; thread!=null; thread=thread.previous) {
+ final long delay = stopWaitingAt - System.nanoTime();
+ if (delay <= 0) break;
+ thread.join(delay);
+ }
+ }
+
+ /**
+ * Returns the names of dead threads, or {@code null} if none. The returned list should
+ * always be null. A non-empty list would be a symptom for a severe problem, probably
+ * requiring an application reboot.
+ *
+ * @param first The first thread in the chain of threads to verify.
+ * @return The name of dead threads, or {@code null} if none.
+ */
+ static List<String> listDeadThreads(final DaemonThread first) {
+ List<String> list = null;
+ for (DaemonThread thread=first; thread!=null; thread=thread.previous) {
+ if (!thread.isAlive()) {
+ if (list == null) {
+ list = new ArrayList<String>();
+ }
+ list.add(thread.getName());
}
}
+ return list;
}
}
Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/ReferenceQueueConsumer.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/ReferenceQueueConsumer.java?rev=1393350&r1=1393349&r2=1393350&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/ReferenceQueueConsumer.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/ReferenceQueueConsumer.java Wed Oct 3 08:40:55 2012
@@ -56,13 +56,15 @@ import org.apache.sis.util.logging.Loggi
*/
public final class ReferenceQueueConsumer<T> extends DaemonThread {
/**
- * The default thread.
+ * The singleton instance of the {@code ReferenceQueueConsumer} thread.
*/
public static final ReferenceQueueConsumer<Object> DEFAULT;
static {
+ synchronized (Threads.class) {
+ Threads.lastCreatedDaemon = DEFAULT = new ReferenceQueueConsumer<Object>(Threads.lastCreatedDaemon);
+ }
// Call to Thread.start() must be outside the constructor
// (Reference: Goetz et al.: "Java Concurrency in Practice").
- DEFAULT = new ReferenceQueueConsumer<Object>("ReferenceQueueConsumer");
DEFAULT.start();
}
@@ -76,18 +78,17 @@ public final class ReferenceQueueConsume
/**
* Constructs a new thread as a daemon thread. This thread will be sleeping most of the time.
* It will run only only a few nanoseconds every time a new {@link Reference} is enqueded.
- *
- * @param name The thread name. This name appears in the debugger.
*/
- private ReferenceQueueConsumer(final String name) {
- super(Threads.RESOURCE_DISPOSERS, name);
- setPriority(Thread.MAX_PRIORITY);
+ private ReferenceQueueConsumer(final DaemonThread lastCreatedDaemon) {
+ super(Threads.RESOURCE_DISPOSERS, "ReferenceQueueConsumer", lastCreatedDaemon);
+ setPriority(Thread.MAX_PRIORITY - 2);
// The above line sets the priority to the maximal value allowed by the
// RESOURCE_DISPOSERS group, which is actually lower than MAX_PRIORITY.
}
/**
* Loop to be run during the virtual machine lifetime.
+ * Public as an implementation side-effect; <strong>do not invoke explicitly!</strong>
*/
@Override
public final void run() {
@@ -117,7 +118,7 @@ public final class ReferenceQueueConsume
continue;
}
} catch (InterruptedException exception) {
- // Probably the 'kill()' method has been invoked.
+ // Probably the 'killAll' method has been invoked.
// We need to test 'isKillRequested()' below.
} catch (Throwable exception) {
Logging.unexpectedException(getClass(), "run", exception);
Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/Threads.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/Threads.java?rev=1393350&r1=1393349&r2=1393350&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/Threads.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/internal/util/Threads.java Wed Oct 3 08:40:55 2012
@@ -16,6 +16,7 @@
*/
package org.apache.sis.internal.util;
+import org.apache.sis.util.Static;
import org.apache.sis.util.logging.Logging;
@@ -29,7 +30,7 @@ import org.apache.sis.util.logging.Loggi
* @version 0.3
* @module
*/
-public final class Threads {
+final class Threads extends Static {
/**
* The parent of every threads declared in this class. This parent will be declared as close
* as possible to the root of all thread groups (i.e. not as an application thread subgroup).
@@ -56,17 +57,40 @@ public final class Threads {
* quickly, and the benefit of executing those tasks soon is more resources made available.
*/
static final ThreadGroup RESOURCE_DISPOSERS = new ThreadGroup(SIS, "ResourceDisposers") {
+ /* Constructor */ {
+ setMaxPriority(Thread.NORM_PRIORITY + 3);
+ }
@Override public void uncaughtException(final Thread thread, final Throwable exception) {
Logging.severeException(Logging.getLogger("org.apache.sis"), thread.getClass(), "run", exception);
}
};
- static {
- RESOURCE_DISPOSERS.setMaxPriority(Thread.NORM_PRIORITY + 2);
- }
+
+ /**
+ * The tail of a chain of {@code DaemonThread}s created by the {@code sis-utility} module.
+ * Other modules need to maintain their own chain, if any. See the {@link DaemonThread}
+ * javadoc for more information.
+ */
+ static DaemonThread lastCreatedDaemon;
/**
* Do not allows instantiation of this class.
*/
private Threads() {
}
+
+ /**
+ * Sends a kill signal to all daemon threads created by the {@code sis-utility} module,
+ * and waits for the threads to die before to return.
+ * <p>
+ * <strong>This method is for internal use by Apache SIS shutdown hooks only.</strong>
+ * Users should never invoke this method explicitely.
+ *
+ * @param stopWaitingAt A {@link System#nanoTime()} value telling when to stop waiting.
+ * This is used for preventing shutdown process to block an indefinite amount of time.
+ * @throws InterruptedException If an other thread invoked {@link #interrupt()} while
+ * we were waiting for the daemon threads to die.
+ */
+ static synchronized void shutdown(final long stopWaitingAt) throws InterruptedException {
+ DaemonThread.killAll(lastCreatedDaemon, stopWaitingAt);
+ }
}