// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package java.lang; import sun.nio.ch.Interruptible; /** * MJI model class for java.lang.Thread library abstraction * * <2do> this should not require the JPF ThreadList to retrieve corresponding * ThreadInfos (the ThreadList might not store terminated threads) */ public class Thread implements Runnable { /** The current seed for a ThreadLocalRandom */ // @sun.misc.Contended("tlr") long threadLocalRandomSeed; /** Probe hash value; nonzero if threadLocalRandomSeed initialized */ // @sun.misc.Contended("tlr") int threadLocalRandomProbe; /** Secondary seed isolated from public ThreadLocalRandom sequence */ // @sun.misc.Contended("tlr") int threadLocalRandomSecondarySeed; // TODO not implemented yet private volatile Interruptible blocker; private final Object blockerLock = new Object(); public interface UncaughtExceptionHandler { // note this doesn't stop the thread from being terminated void uncaughtException(Thread t, Throwable x); } static int nameThreadNum; // to construct the default thread name public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10; // don't rename this - it's used by ThreadGoup.uncaughtException() private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; // null // by // default // initialized in init(), except of the main thread (which gets explicitly // initialized by the VM) ThreadGroup group; Runnable target; private char[] name; int priority; boolean isDaemon; // this is an explicit thread state that gets set on a call of interrupt(), // but // only if the thread is not blocked. If it is, we only change the status. // this gets cleared by calling interrupted() boolean interrupted; // <2do> those two seem to be the only interfaces to the ThreadLocal // implementation. Replace once we have our own // ThreadLocal / InhertitableThreadLocal classes ThreadLocal.ThreadLocalMap threadLocals; ThreadLocal.ThreadLocalMap inheritableThreadLocals; // this is what we use for sun.misc.Unsafe.park()/unpark() // this is accessed from the native peer, VM.createMainThread() and // sun.misc.Unsafe static class Permit { boolean blockPark = true; // this is used to remember unpark() calls // before park() (they don't accumulate) } Permit permit; // the object is used for wait/notify // referenced by java.util.concurrent.locks.LockSupport via sun.misc.Unsafe // DON'T CHANGE THIS NAME volatile Object parkBlocker; // used to store Thread.stop() exceptions Throwable stopException; private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null // by // default public enum State { BLOCKED, NEW, RUNNABLE, TERMINATED, TIMED_WAITING, WAITING } public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler xh) { defaultUncaughtExceptionHandler = xh; } public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { return defaultUncaughtExceptionHandler; } public Thread() { this(null, null, null, 0L); } public Thread(Runnable target) { this(null, target, null, 0L); } public Thread(Runnable target, String name) { this(null, target, name, 0L); } public Thread(String name) { this(null, null, name, 0L); } public Thread(ThreadGroup group, String name) { this(group, null, name, 0L); } public Thread(ThreadGroup group, Runnable target) { this(group, target, null, 0L); } public Thread(ThreadGroup group, Runnable target, String name) { this(group, target, name, 0L); } public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { Thread cur = currentThread(); if (group == null) { this.group = cur.getThreadGroup(); } else { this.group = group; } this.group.add(this); if (name == null) { this.name = ("Thread-" + ++nameThreadNum).toCharArray(); } else { this.name = name.toCharArray(); } this.permit = new Permit(); // those are always inherited from the current thread this.priority = cur.getPriority(); this.isDaemon = cur.isDaemon(); this.target = target; // do our associated native init init0(this.group, target, new String(this.name), stackSize); } // this takes care of ThreadInfo initialization native void init0(ThreadGroup group, Runnable target, String name, long stackSize); public static int activeCount() { return 0; } public void setUncaughtExceptionHandler(UncaughtExceptionHandler xh) { uncaughtExceptionHandler = xh; } public UncaughtExceptionHandler getUncaughtExceptionHandler() { if (uncaughtExceptionHandler != null) { return uncaughtExceptionHandler; } else { return group; } } public void setContextClassLoader(ClassLoader cl) { } public ClassLoader getContextClassLoader() { // <NSY> // FIXME: jetty call this to load webapp classes return null; } public synchronized void setDaemon(boolean isDaemon) { this.isDaemon = isDaemon; setDaemon0(isDaemon); } public boolean isDaemon() { return isDaemon; } public native long getId(); public StackTraceElement[] getStackTrace() { return null; // not yet implemented } public native int getState0(); public Thread.State getState() { int i = getState0(); switch (i) { case 0: return State.BLOCKED; case 1: return State.NEW; case 2: return State.RUNNABLE; case 3: return State.TERMINATED; case 4: return State.TIMED_WAITING; case 5: return State.WAITING; } return null; // shoudl be intercepted by a getState0 assertion } public synchronized void setName(String name) { if (name == null) { throw new IllegalArgumentException("thread name can't be null"); } this.name = name.toCharArray(); setName0(name); } public String getName() { // return ""; return new String(name); } public void setPriority(int priority) { if ((priority < MIN_PRIORITY) || (priority > MAX_PRIORITY)) { throw new IllegalArgumentException("thread priority out of range"); } this.priority = priority; setPriority0(priority); } public int getPriority() { return priority; } public ThreadGroup getThreadGroup() { return group; } public void checkAccess() { // <NSY> } public native int countStackFrames(); public static native Thread currentThread(); public void destroy() { } public static void dumpStack() { } public static int enumerate(Thread[] tarray) { Thread cur = currentThread(); return cur.group.enumerate(tarray); } public static native boolean holdsLock(Object obj); // this one needs to be native because it might change the thread status public void interrupt() { if (this != Thread.currentThread()) checkAccess(); if (blockerLock != null) { synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } } interrupt0(); } public native void interrupt0(); // those don't have to be native, but we keep it symmetric public static native boolean interrupted(); public native boolean isInterrupted(); public native boolean isAlive(); /** * note these are not synchronized anymore since they are intercepted by the * native peer. The reason is that we don't want two CGs per join call (one * for the sync call, and one for the wait) because this can cause serious * performance degradation */ public void join() throws InterruptedException { synchronized (this) { if (interrupted()) { throw new InterruptedException(); } while (isAlive()) { // apparently, the JDK doesn't throw InterruptedExceptions if // we get interrupted after waiting in the join wait(); } } } public void join(long millis) throws InterruptedException { join(millis, 0); } public void join(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new java.lang.IllegalArgumentException("timeout value is negative"); } else if (millis == 0) { join(); } else { synchronized (this) { if (interrupted()) { throw new InterruptedException(); } wait(millis); } } } public void run() { if (target != null) { target.run(); } } public static void sleep(long millis) throws InterruptedException { sleep(millis, 0); } public static native void sleep(long millis, int nanos) throws InterruptedException; public native void start(); public native void stop(); public native void stop(Throwable obj); public native void suspend(); public native void resume(); public String toString() { return ("Thread[" + new String(name) + ',' + priority + ',' + (group == null ? "" : group.getName()) + ']'); } public static native void yield(); native void setDaemon0(boolean on); native void setName0(String name); native void setPriority0(int priority); /** * automatically called by system upon thread termination to clean up * references. * * NOTE - we clean up atomically during ThreadInfo.finish(), to avoid any * additional states. This is important since group per se is a shared * object We only include this method here as a specification for ThreadInfo */ private void exit() { if (group != null) { group.threadTerminated(this); group = null; } threadLocals = null; inheritableThreadLocals = null; parkBlocker = null; uncaughtExceptionHandler = null; } void blockedOn(Interruptible b) { synchronized (blockerLock) { blocker = b; } } }