package com.entityreborn.socpuppet.util;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Thread started on shutdown that monitors for and kills rogue non-daemon threads.
*/
public class ShutdownMonitorThread extends Thread {
/**
* The delay in milliseconds until leftover threads are killed.
*/
private static final int DELAY = 2000;
private Runnable postShutdown = null;
public ShutdownMonitorThread() {
setName("ShutdownMonitorThread");
}
public ShutdownMonitorThread(Runnable postCall) {
this();
postShutdown = postCall;
}
@Override
public void run() {
final Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
final Map<Thread, StackTraceElement[]> roguetraces = new HashMap<>();
for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
final Thread thread = entry.getKey();
final StackTraceElement[] stack = entry.getValue();
if (thread == this || thread.isDaemon() || !thread.isAlive() || stack.length == 0) {
// won't keep JVM from exiting
continue;
}
roguetraces.put(thread, stack);
}
try {
if (!roguetraces.isEmpty()) {
Thread.sleep(DELAY);
}
} catch (InterruptedException e) {
Logger.getGlobal().log(Level.SEVERE, "Shutdown monitor interrupted", e);
doPostCall();
return;
}
for (Map.Entry<Thread, StackTraceElement[]> entry : roguetraces.entrySet()) {
final Thread thread = entry.getKey();
if (thread == this) {
continue;
}
final StackTraceElement[] stack = entry.getValue();
Logger.getGlobal().warning("Rogue thread: " + thread);
for (StackTraceElement trace : stack) {
Logger.getGlobal().warning(" at " + trace);
}
// ask nicely to kill them
thread.interrupt();
// wait for them to die on their own
if (thread.isAlive()) {
try {
thread.join(1000);
} catch (InterruptedException ex) {
Logger.getGlobal().log(Level.SEVERE, "Shutdown monitor interrupted", ex);
doPostCall();
return;
}
}
}
doPostCall();
}
private void doPostCall() {
if (postShutdown != null) {
try {
postShutdown.run();
} catch (Throwable t) {
Logger.getGlobal().log(Level.SEVERE, "Shutdown monitor post-Shutdown generated an exception", t);
}
}
}
}