/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.support.io; import freenet.support.LibraryLoader; import freenet.support.Logger; /** * Do *NOT* forget to call super.run() if you extend it! * * @see <a href="https://emu.freenetproject.org/pipermail/devl/2008-February/028357.html">Devl Mailing List</a> * @author Florent Daignière <nextgens@freenetproject.org> */ public class NativeThread extends Thread { public static final boolean _loadNative; private static boolean _disabled; public static final int JAVA_PRIORITY_RANGE = Thread.MAX_PRIORITY - Thread.MIN_PRIORITY; private final static int NATIVE_PRIORITY_BASE; public final static int NATIVE_PRIORITY_RANGE; private int currentPriority = Thread.MAX_PRIORITY; private boolean dontCheckRenice = false; public final static boolean HAS_THREE_NICE_LEVELS; public final static boolean HAS_ENOUGH_NICE_LEVELS; public final static boolean HAS_PLENTY_NICE_LEVELS; // TODO: Wire in. public static enum PriorityLevel { MIN_PRIORITY(1), LOW_PRIORITY(3), NORM_PRIORITY(5), HIGH_PRIORITY(7), MAX_PRIORITY(10); public final int value; PriorityLevel(int myValue) { value = myValue; } public static PriorityLevel fromValue(int value) { for(PriorityLevel level :PriorityLevel.values()) { if(level.value == value) return level; } throw new IllegalArgumentException(); } } public static final int ENOUGH_NICE_LEVELS = PriorityLevel.values().length; @Deprecated public static final int MIN_PRIORITY = PriorityLevel.MIN_PRIORITY.value; @Deprecated public static final int LOW_PRIORITY = PriorityLevel.LOW_PRIORITY.value; @Deprecated public static final int NORM_PRIORITY = PriorityLevel.NORM_PRIORITY.value; @Deprecated public static final int HIGH_PRIORITY = PriorityLevel.HIGH_PRIORITY.value; @Deprecated public static final int MAX_PRIORITY = PriorityLevel.MAX_PRIORITY.value; static { Logger.minor(NativeThread.class, "Running init()"); // Loading the NativeThread library isn't useful on macos boolean maybeLoadNative = ("Linux".equalsIgnoreCase(System.getProperty("os.name"))); Logger.debug(NativeThread.class, "Run init(): should loadNative="+maybeLoadNative); if(maybeLoadNative && LibraryLoader.loadNative("/freenet/support/io/", "NativeThread")) { NATIVE_PRIORITY_BASE = getLinuxPriority(); NATIVE_PRIORITY_RANGE = 20 - NATIVE_PRIORITY_BASE; System.out.println("Using the NativeThread implementation (base nice level is "+NATIVE_PRIORITY_BASE+')'); // they are 3 main prio levels HAS_THREE_NICE_LEVELS = NATIVE_PRIORITY_RANGE >= 3; HAS_ENOUGH_NICE_LEVELS = NATIVE_PRIORITY_RANGE >= ENOUGH_NICE_LEVELS; HAS_PLENTY_NICE_LEVELS = NATIVE_PRIORITY_RANGE >=JAVA_PRIORITY_RANGE; if(!(HAS_ENOUGH_NICE_LEVELS && HAS_THREE_NICE_LEVELS)) System.err.println("WARNING!!! The JVM has been niced down to a level which won't allow it to schedule threads properly! LOWER THE NICE LEVEL!!"); _loadNative = true; } else { // unused anyway NATIVE_PRIORITY_BASE = 0; NATIVE_PRIORITY_RANGE = 19; HAS_THREE_NICE_LEVELS = true; HAS_ENOUGH_NICE_LEVELS = true; HAS_PLENTY_NICE_LEVELS = true; _loadNative = false; } Logger.minor(NativeThread.class, "Run init(): _loadNative = "+_loadNative); } /** * Creates a new native (reniced) thread * * @param name * @param priority * @param dontCheckRenice This should be set to true * unless the caller is running at NATIVE_PRIORITY_BASE @see bug6623 */ public NativeThread(String name, int priority, boolean dontCheckRenice) { super(name); this.currentPriority = priority; this.dontCheckRenice = dontCheckRenice; } /** * Creates a new native (reniced) thread * * @param name * @param priority * @param dontCheckRenice This should be set to true * unless the caller is running at NATIVE_PRIORITY_BASE @see bug6623 */ public NativeThread(Runnable r, String name, int priority, boolean dontCheckRenice) { super(r, name); this.currentPriority = priority; this.dontCheckRenice = dontCheckRenice; } /** * Creates a new native (reniced) thread * * @param name * @param priority * @param dontCheckRenice This should be set to true * unless the caller is running at NATIVE_PRIORITY_BASE @see bug6623 */ public NativeThread(ThreadGroup g, Runnable r, String name, int priority, boolean dontCheckRenice) { super(g, r, name); this.currentPriority = priority; this.dontCheckRenice = dontCheckRenice; } /** * Set linux priority (JNI call) * * @return true if successful, false otherwise. */ private static native boolean setLinuxPriority(int prio); /** * Get linux priority (JNI call) */ private static native int getLinuxPriority(); @Override public final void run() { if(!setNativePriority(currentPriority)) System.err.println("setNativePriority("+currentPriority+") has failed!"); super.run(); realRun(); } public void realRun() { // Override this for convenience when doing new NativeThread() { ... } } /** * Rescale java priority and set linux priority. */ private boolean setNativePriority(int prio) { Logger.minor(this, "setNativePriority("+prio+")"); setPriority(prio); if(!_loadNative) { Logger.minor(this, "_loadNative is false"); return true; } int realPrio = getLinuxPriority(); if(_disabled) { Logger.normal(this, "Not setting native priority as disabled due to renicing"); return false; } if(NATIVE_PRIORITY_BASE != realPrio && !dontCheckRenice) { /* The user has reniced freenet or we didn't use the PacketSender to create the thread * either ways it's bad for us. * * Let's disable the renicing as we can't rely on it anymore. */ _disabled = true; Logger.error(this, "Freenet has detected it has been reniced : THAT'S BAD, DON'T DO IT! Nice level detected statically: "+NATIVE_PRIORITY_BASE+" actual nice level: "+realPrio+" on "+this); System.err.println("Freenet has detected it has been reniced : THAT'S BAD, DON'T DO IT! Nice level detected statically: "+NATIVE_PRIORITY_BASE+" actual nice level: "+realPrio+" on "+this); new NullPointerException().printStackTrace(); return false; } final int linuxPriority = NATIVE_PRIORITY_BASE + NATIVE_PRIORITY_RANGE - (NATIVE_PRIORITY_RANGE * (prio - MIN_PRIORITY)) / JAVA_PRIORITY_RANGE; if(linuxPriority == realPrio) return true; // Ok // That's an obvious coding mistake if(prio < currentPriority) throw new IllegalStateException("You're trying to set a thread priority" + " above the current value!! It's not possible if you aren't root" + " and shouldn't ever occur in our code. (asked="+prio+':'+linuxPriority+" currentMax="+ +currentPriority+':'+NATIVE_PRIORITY_BASE+") SHOUDLN'T HAPPEN, please report!"); Logger.minor(this, "Setting native priority to "+linuxPriority+" (base="+NATIVE_PRIORITY_BASE+") for "+this); return setLinuxPriority(linuxPriority); } public int getNativePriority() { return currentPriority; } public static boolean usingNativeCode() { return _loadNative && !_disabled; } public static String normalizeName(String name) { if(name.indexOf(" for ") != -1) name = name.substring(0, name.indexOf(" for ")); if(name.indexOf('@') != -1) name = name.substring(0, name.indexOf('@')); if (name.indexOf('(') != -1) name = name.substring(0, name.indexOf('(')); return name.trim(); } public String getNormalizedName() { return normalizeName(getName()); } }