package net.i2p.util;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Preferred over {@link Thread} for all router uses.
* For applications, {@link I2PAppThread} is preferred.
* <p>
* Provides the following features:
* <ul>
* <li>Logging to wrapper log on unexpected termination in {@link #run()}.
* <li>Notification of OOM to registered listener (the router),
* which will cause logging to the wrapper log and a router restart
* <li>Catching and logging "OOM" caused by thread limit in {@link #start()}
* with distinct message, and does not call the OOM listener.
* <li>As of 0.9.21, initialization to NORM_PRIORITY
* (not the priority of the creating thread).
* </ul>
*/
public class I2PThread extends Thread {
private static final Set<OOMEventListener> _listeners = new CopyOnWriteArraySet<OOMEventListener>();
public I2PThread() {
super();
setPriority(NORM_PRIORITY);
}
public I2PThread(String name) {
super(name);
setPriority(NORM_PRIORITY);
}
public I2PThread(Runnable r) {
super(r);
setPriority(NORM_PRIORITY);
}
public I2PThread(Runnable r, String name) {
super(r, name);
setPriority(NORM_PRIORITY);
}
public I2PThread(Runnable r, String name, boolean isDaemon) {
super(r, name);
setDaemon(isDaemon);
setPriority(NORM_PRIORITY);
}
public I2PThread(ThreadGroup g, Runnable r) {
super(g, r);
setPriority(NORM_PRIORITY);
}
/**
* @since 0.9.23
*/
public I2PThread(ThreadGroup group, Runnable r, String name) {
super(group, r, name);
setPriority(NORM_PRIORITY);
}
/**
* Overridden to provide useful info to users on OOM, and to prevent
* shutting down the whole JVM for what is most likely not a heap issue.
* If the calling thread is an I2PThread an OOM would shut down the JVM.
* Telling the user to increase the heap size may make the problem worse.
* We may be able to continue without this thread, particularly in app context.
*
* @since 0.9.20
*/
@Override
public void start() {
try {
super.start();
} catch (OutOfMemoryError oom) {
System.out.println("ERROR: Thread could not be started: " + getName());
if (!(SystemVersion.isWindows() || SystemVersion.isAndroid())) {
System.out.println("Check ulimit -u, /etc/security/limits.conf, or /proc/sys/kernel/threads-max");
}
oom.printStackTrace();
if (!(SystemVersion.isWindows() || SystemVersion.isAndroid()))
throw new RuntimeException("Thread could not be started, " +
"Check ulimit -u, /etc/security/limits.conf, or /proc/sys/kernel/threads-max", oom);
throw new RuntimeException("Thread could not be started", oom);
}
}
@Override
public void run() {
try {
super.run();
} catch (Throwable t) {
if (t instanceof OutOfMemoryError) {
fireOOM((OutOfMemoryError)t);
} else {
System.out.println ("Thread terminated unexpectedly: " + getName());
t.printStackTrace();
}
}
}
protected void fireOOM(OutOfMemoryError oom) {
for (OOMEventListener listener : _listeners)
listener.outOfMemory(oom);
}
/** register a new component that wants notification of OOM events */
public static void addOOMEventListener(OOMEventListener lsnr) {
_listeners.add(lsnr);
}
/** unregister a component that wants notification of OOM events */
public static void removeOOMEventListener(OOMEventListener lsnr) {
_listeners.remove(lsnr);
}
public interface OOMEventListener {
public void outOfMemory(OutOfMemoryError err);
}
/****
public static void main(String args[]) {
I2PThread t = new I2PThread(new Runnable() {
public void run() {
throw new NullPointerException("blah");
}
});
t.start();
try {
Thread.sleep(10000);
} catch (Throwable tt) { // nop
}
}
****/
}