/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.scheduler; import org.jikesrvm.VM_CodeArray; import org.jikesrvm.ArchitectureSpecific; import static org.jikesrvm.ArchitectureSpecific.VM_StackframeLayoutConstants.INVISIBLE_METHOD_ID; import static org.jikesrvm.ArchitectureSpecific.VM_StackframeLayoutConstants.STACKFRAME_SENTINEL_FP; import static org.jikesrvm.ArchitectureSpecific.VM_StackframeLayoutConstants.STACK_SIZE_GUARD; import org.jikesrvm.SubordinateArchitecture; import org.jikesrvm.VM; import org.jikesrvm.VM_Configuration; import org.jikesrvm.VM_Registers; import org.jikesrvm.VM_Services; import org.jikesrvm.VM_UnimplementedError; import org.jikesrvm.adaptive.OSR_OnStackReplacementEvent; import org.jikesrvm.adaptive.measurements.VM_RuntimeMeasurements; import org.jikesrvm.annotations.NoSubArchCompile; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.common.VM_CompiledMethods; import org.jikesrvm.jni.VM_JNIEnvironment; import org.jikesrvm.memorymanagers.mminterface.MM_Interface; import org.jikesrvm.objectmodel.VM_ObjectModel; import org.jikesrvm.objectmodel.VM_ThinLockConstants; import org.jikesrvm.runtime.VM_Entrypoints; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_Memory; import org.jikesrvm.runtime.VM_Runtime; import org.jikesrvm.runtime.VM_Time; import org.vmmagic.pragma.BaselineNoRegisters; import org.vmmagic.pragma.BaselineSaveLSRegisters; import org.vmmagic.pragma.Entrypoint; import org.vmmagic.pragma.Interruptible; import org.vmmagic.pragma.LogicallyUninterruptible; import org.vmmagic.pragma.NoInline; import org.vmmagic.pragma.NoOptCompile; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.LocalAddress; import org.vmmagic.unboxed.Offset; /** * A generic java thread's execution context. * * @see org.jikesrvm.scheduler.greenthreads.VM_GreenThread * @see org.jikesrvm.memorymanagers.mminterface.VM_CollectorThread * @see VM_DebuggerThread * @see VM_FinalizerThread * @see org.jikesrvm.adaptive.measurements.organizers.VM_Organizer */ @Uninterruptible @NoSubArchCompile public abstract class VM_Thread { /* * debug and statistics */ /** Trace execution */ protected static final boolean trace = false; /** Trace thread termination */ private static final boolean traceTermination = false; /** Trace adjustments to stack size */ private static final boolean traceAdjustments = false; /** Generate statistics? */ private static final boolean STATS = VM_Lock.STATS; /** Number of wait operations */ static int waitOperations; /** Number of timed wait operations */ static int timedWaitOperations; /** Number of notify operations */ static int notifyOperations; /** Number of notifyAll operations */ static int notifyAllOperations; /** * The thread is modeled by a state machine the following constants describe * the state of the state machine. Invalid transitions will generate * IllegalThreadStateExceptions. */ protected static enum State { /** * The thread is created but not yet scheduled to run. This state is the * same as {@link Thread.State#NEW} */ NEW, /** * The thread is scheduled to run on a VM_Processor. This state is the same * as {@link Thread.State#RUNNABLE} */ RUNNABLE, /** * The thread is blocked by waiting for a monitor lock. This state is the * same as {@link Thread.State#BLOCKED} */ BLOCKED, /** * The thread is waiting indefintely for a notify. This state maps to * {@link Thread.State#WAITING} */ WAITING, /** * The thread is waiting for a notify or a time out. This state maps to * {@link Thread.State#TIMED_WAITING} */ TIMED_WAITING, /** * The thread has exited. This state maps to {@link Thread.State#TERMINATED} */ TERMINATED, /** * The thread is waiting for a notify or a time out. This state maps to * {@link Thread.State#TIMED_WAITING} */ SLEEPING, /** * The thread is suspended awaiting a resume. This state maps to * {@link Thread.State#WAITING} which makes better sense than the JDK 1.5 * convention */ SUSPENDED, /** * The thread is suspended awaiting a resume. This state maps to * {@link Thread.State#WAITING} */ OSR_SUSPENDED, /** * The thread is awaiting this thread to become RUNNABLE. This state maps to * {@link Thread.State#WAITING} matching JDK 1.5 convention */ JOINING, /** * The thread is parked awaiting a unpark. This state maps to * {@link Thread.State#WAITING} matching JDK 1.5 convention */ PARKED, /** * The thread is parked awaiting a unpark. This state maps to * {@link Thread.State#TIMED_WAITING} matching JDK 1.5 convention */ TIMED_PARK, /** * This state is valid only for green threads. The thread is awaiting IO to * readable. This state maps to {@link Thread.State#WAITING}. */ IO_WAITING, /** * This state is valid only for green threads. The thread is awaiting a * process to finish. This state maps to {@link Thread.State#WAITING}. */ PROCESS_WAITING, /** * This state is valid only for green threads. The thread is awaiting a * subarch migration to finish. This state maps to {@link Thread.State#WAITING}. */ SUBARCH_WAITING } /** * State of the thread. Either NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, * TERMINATED, SLEEPING, SUSPENDED or PARKED */ protected State state; /** * java.lang.Thread wrapper for this VM_Thread. Not final so it may be * assigned during booting */ private Thread thread; /** Name of the thread (can be changed during execution) */ private String name; /** * The virtual machine terminates when the last non-daemon (user) * thread terminates. */ protected boolean daemon; /** * Should the thread throw the external interrupt object the next time it's scheduled? */ protected volatile boolean throwInterruptWhenScheduled; /** * Has the thread been interrupted? We should throw an interrupted exception * when we next get to an interruptible operation */ protected volatile boolean interrupted; /** * The cause of the thread interruption (stop) or interruption */ protected volatile Throwable causeOfThreadDeath; /** * Scheduling priority for this thread. * Note that: {@link java.lang.Thread#MIN_PRIORITY} <= priority * <= {@link java.lang.Thread#MAX_PRIORITY}. */ private int priority; /** * If true, then this thread will be run on the sub architecture */ private boolean forSubArch; /** * Index of this thread in {@link VM_Scheduler#threads}[]. * This value must be non-zero because it is shifted * and used in {@link Object} lock ownership tests. */ @Entrypoint private final int threadSlot; /** * Is this thread's stack being "borrowed" by thread dispatcher * (ie. while choosing next thread to run)? */ @Entrypoint public boolean beingDispatched; /** * Thread is a system thread, that is one used by the system and as * such doesn't have a Runnable... */ final boolean systemThread; /** * The boot thread, can't be final so as to allow initialization during boot * image writing. */ private static VM_Thread bootThread; /** * Assertion checking while manipulating raw addresses -- * see {@link VM#disableGC()}/{@link VM#enableGC()}. * A value of "true" means it's an error for this thread to call "new". * This is only used for assertion checking; we do not bother to set it when * {@link VM#VerifyAssertions} is false. */ private boolean disallowAllocationsByThisThread; /** * Counts the depth of outstanding calls to {@link VM#disableGC()}. If this * is set, then we should also have {@link #disallowAllocationsByThisThread} * set. The converse also holds. */ private int disableGCDepth = 0; /** * Execution stack for this thread. */ @Entrypoint private byte[] stack; /** The {@link Address} of the guard area for {@link #stack}. */ @Entrypoint public LocalAddress stackLimit; /** * Place to save register state when this thread is not actually running. */ @Entrypoint public final VM_Registers contextRegisters; /** * Place to save register state when C signal handler traps * an exception while this thread is running. */ @Entrypoint private final VM_Registers hardwareExceptionRegisters; /** Count of recursive uncaught exceptions, we need to bail out at some point */ private int uncaughtExceptionCount = 0; /* * Wait/notify fields */ /** * Place to save/restore this thread's monitor state during * {@link Object#wait} and {@link Object#notify}. */ protected Object waitObject; /** Lock recursion count for this thread's monitor. */ protected int waitCount; /* * Sleep fields */ /** * If this thread is sleeping, when should it be awakened? */ protected long wakeupNanoTime; /* * Parking fields */ /** Is a running thread permitted to ignore the next park request */ private boolean parkingPermit; /** * An interrupted parked thread never sees the stack trace so use a proxy * interrupt exception in all cases. Also used to substitute for an * interrupted exception when we're in uninterruptible code and unable to * create one. */ protected static final InterruptedException proxyInterruptException = new InterruptedException("park interrupted"); /* * JNI fields */ /** * Cached JNI environment for this thread */ @Entrypoint public VM_JNIEnvironment jniEnv; /* * Timing fields */ /** * Per thread timing is only active when this field has a value greater than 0. */ private int timingDepth = 0; /** * Value returned from {@link VM_Time#nanoTime()} when this thread * started running. If not currently running, then it has the value 0. */ private long startNano = 0; /** * Accumulated nanoTime as measured by {@link VM_Time#nanoTime()} * used by this thread. */ private long totalNanos = 0; /** Used by GC to determine collection success */ private boolean physicalAllocationFailed; /** Is this thread performing emergency allocation, when the normal heap limits are ignored. */ private boolean emergencyAllocation; /** Used by GC to determine collection success */ private int collectionAttempt; /** The OOME to throw */ private OutOfMemoryError outOfMemoryError; /* * Enumerate different types of yield points for sampling */ public static final int PROLOGUE = 0; public static final int BACKEDGE = 1; public static final int EPILOGUE = 2; public static final int NATIVE_PROLOGUE = 3; public static final int NATIVE_EPILOGUE = 4; public static final int OSROPT = 5; /* * Fields used for on stack replacement */ /** * Only used by OSR when VM.BuildForAdaptiveSystem. Declared as an * Object to cut link to adaptive system. Ugh. */ public final Object /* OSR_OnStackReplacementEvent */ onStackReplacementEvent; /** * The flag indicates whether this thread is waiting for on stack * replacement before being rescheduled. */ //flags should be packaged or replaced by other solutions public boolean isWaitingForOsr = false; /** * Before call new instructions, we need a bridge to recover * register states from the stack frame. */ public VM_CodeArray bridgeInstructions = null; /** Foo frame pointer offset */ public Offset fooFPOffset = Offset.zero(); /** Thread switch frame pointer offset */ public Offset tsFPOffset = Offset.zero(); /** * Flag to synchronize with osr organizer, the trigger sets osr * requests the organizer clear the requests */ public boolean requesting_osr = false; /** * Is the system in the process of shutting down (has System.exit been called) */ private static boolean systemShuttingDown = false; /** * @param stack stack in which to execute the thread */ protected VM_Thread(byte[] stack, Thread thread, String name, boolean daemon, boolean system, int priority, boolean forSubArch) { this.stack = stack; this.name = name; this.daemon = daemon; this.priority = priority; this.forSubArch = forSubArch; if (!forSubArch) { contextRegisters = new ArchitectureSpecific.VM_Registers(); hardwareExceptionRegisters = new ArchitectureSpecific.VM_Registers(); } else { contextRegisters = new SubordinateArchitecture.VM_Registers(); hardwareExceptionRegisters = new SubordinateArchitecture.VM_Registers(); } if(VM.VerifyAssertions) VM._assert(stack != null); // put self in list of threads known to scheduler and garbage collector if (!VM.runningVM) { // create primordial thread (in boot image) threadSlot = VM_Scheduler.assignThreadSlot(this); // Remember the boot thread if (VM.VerifyAssertions) VM._assert(bootThread == null); bootThread = this; this.systemThread = true; this.state = State.RUNNABLE; // assign final field onStackReplacementEvent = null; } else { // create a normal (ie. non-primordial) thread if (trace) VM_Scheduler.trace("VM_Thread create: ", name); if (trace) VM_Scheduler.trace("daemon: ", daemon ? "true" : "false"); if (trace) VM_Scheduler.trace("VM_Thread", "create"); // set up wrapper Thread if one exists this.thread = thread; // Set thread type this.systemThread = system; this.state = State.NEW; stackLimit = VM_Magic.objectAsLocalAddress(stack).plus(STACK_SIZE_GUARD); // get instructions for method to be executed as thread startoff VM_CodeArray instructions = VM_Entrypoints.threadStartoffMethod.getCurrentEntryCodeArray(forSubArch); VM.disableGC(); // initialize thread registers LocalAddress ip = VM_Magic.objectAsLocalAddress(instructions); LocalAddress sp = VM_Magic.objectAsLocalAddress(stack).plus(stack.length); // Initialize the a thread stack as if "startoff" method had been called // by an empty baseline-compiled "sentinel" frame with one local variable. VM_Configuration.archHelper.initializeStack(contextRegisters, ip, sp); threadSlot = VM_Scheduler.assignThreadSlot(this); VM.enableGC(); // only do this at runtime because it will call VM_Magic; // we set this explicitly for the boot thread as part of booting. jniEnv = VM_JNIEnvironment.allocateEnvironment(); if (VM.BuildForAdaptiveSystem) { onStackReplacementEvent = new OSR_OnStackReplacementEvent(); } else { onStackReplacementEvent = null; } if (thread == null) { // create wrapper Thread if doesn't exist this.thread = java.lang.JikesRVMSupport.createThread(this, name); } } } /** * Called during booting to give the boot thread a java.lang.Thread */ @Interruptible public final void setupBootThread() { thread = java.lang.JikesRVMSupport.createThread(this, "Jikes_RVM_Boot_Thread"); } /** * String representation of thread */ @Override @Interruptible public String toString() { return (name == null) ? "VM_Thread-" + getIndex() : name; } /** * Get the current java.lang.Thread. */ @Interruptible public final Thread getJavaLangThread() { if (VM.VerifyAssertions) VM._assert(thread != null); return thread; } /** * Get current thread's JNI environment. */ public final VM_JNIEnvironment getJNIEnv() { return jniEnv; } /** Get the disable GC depth */ public final int getDisableGCDepth() { return disableGCDepth; } /** Modify the disable GC depth */ public final void setDisableGCDepth(int d) { disableGCDepth = d; } /** Are allocations allowed by this thread? */ public final boolean getDisallowAllocationsByThisThread() { return disallowAllocationsByThisThread; } /** Disallow allocations by this thread */ public final void setDisallowAllocationsByThisThread() { disallowAllocationsByThisThread = true; } /** Allow allocations by this thread */ public final void clearDisallowAllocationsByThisThread() { disallowAllocationsByThisThread = false; } /** * Initialize JNI environment for system threads. Called by VM.finishBooting */ @Interruptible public final void initializeJNIEnv() { jniEnv = VM_JNIEnvironment.allocateEnvironment(); } /** * Indicate whether the stack of this VM_Thread contains any C frame * (used in VM_Runtime.deliverHardwareException for stack resize) * @return false during the prolog of the first Java to C transition * true afterward */ public final boolean hasNativeStackFrame() { return jniEnv != null && jniEnv.hasNativeStackFrame(); } /** * Change the state of the thread and fail if we're not in the expected thread * state. This method is logically uninterruptible as we should never be in * the wrong state * * @param oldState the previous thread state * @param newState the new thread state */ @LogicallyUninterruptible @Entrypoint protected final void changeThreadState(State oldState, State newState) { if (trace) { VM.sysWrite("VM_Thread.changeThreadState: thread=", threadSlot, name); VM.sysWrite(" current=", java.lang.JikesRVMSupport.getEnumName(state)); VM.sysWrite(" old=", java.lang.JikesRVMSupport.getEnumName(oldState)); VM.sysWriteln(" new=", java.lang.JikesRVMSupport.getEnumName(newState)); } if (state == oldState) { state = newState; } else { throw new IllegalThreadStateException("Illegal thread state change from " + oldState + " to " + newState + " when in state " + state + " in thread " + name); } } /* * Starting and ending threads */ /** * Method to be executed when this thread starts running. Calls * java.lang.Thread.run but system threads can override directly. */ @Interruptible @Entrypoint public synchronized void run() { try { synchronized(thread) { Throwable t = java.lang.JikesRVMSupport.getStillBorn(thread); if(t != null) { java.lang.JikesRVMSupport.setStillBorn(thread, null); throw t; } } thread.run(); } catch(Throwable t) { try { Thread.UncaughtExceptionHandler handler; handler = thread.getUncaughtExceptionHandler(); handler.uncaughtException(thread, t); } catch(Throwable ignore) { } } } /** * Begin execution of current thread by calling its "run" method. This method * is at the bottom of all created method's stacks. */ @Interruptible @SuppressWarnings({"unused", "UnusedDeclaration"}) // Called by back-door methods. private static void startoff() { VM_Thread currentThread = VM_Scheduler.getCurrentThread(); if (trace) { VM.sysWriteln("VM_Thread.startoff(): about to call ", currentThread.toString(), ".run()"); } try { currentThread.run(); } finally { if (trace) { VM.sysWriteln("VM_Thread.startoff(): finished ", currentThread.toString(), ".run()"); } currentThread.terminate(); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } } /** * Put this thread on ready queue for subsequent execution on a future * timeslice. * Assumption: VM_Thread.contextRegisters are ready to pick up execution * ie. return to a yield or begin thread startup code */ public abstract void schedule(); /** * Update internal state of Thread and Scheduler to indicate that * a thread is about to start */ protected abstract void registerThreadInternal(); /** * Update internal state of Thread and Scheduler to indicate that * a thread is about to start */ public final void registerThread() { changeThreadState(State.NEW, State.RUNNABLE); registerThreadInternal(); } /** * Start execution of 'this' by putting it on the appropriate queue * of an unspecified virtual processor. */ @Interruptible public final void start() { registerThread(); schedule(); } /** * Terminate execution of current thread by abandoning all references to it * and resuming execution in some other (ready) thread. */ @Interruptible public final void terminate() { if (VM.VerifyAssertions) VM._assert(VM_Scheduler.getCurrentThread() == this); boolean terminateSystem = false; if (trace) VM_Scheduler.trace("VM_Thread", "terminate"); if (traceTermination) { VM.disableGC(); VM.sysWriteln("[ BEGIN Verbosely dumping stack at time of thread termination"); VM_Scheduler.dumpStack(); VM.sysWriteln("END Verbosely dumping stack at time of creating thread termination ]"); VM.enableGC(); } if (VM.BuildForAdaptiveSystem) { VM_RuntimeMeasurements.monitorThreadExit(); } // allow java.lang.Thread.exit() to remove this thread from ThreadGroup java.lang.JikesRVMSupport.threadDied(thread); if (VM.VerifyAssertions) { if (VM_Lock.countLocksHeldByThread(getLockingId()) > 0) { VM.sysWriteln("Error, thread terminating holding a lock"); VM_Scheduler.dumpVirtualMachine(); } } // begin critical section // VM_Scheduler.threadCreationMutex.lock("thread termination"); VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for thread termination"); // // if the thread terminated because of an exception, remove // the mark from the exception register object, or else the // garbage collector will attempt to relocate its ip field. hardwareExceptionRegisters.inuse = false; VM_Scheduler.numActiveThreads -= 1; if (daemon) { VM_Scheduler.numDaemons -= 1; } if ((VM_Scheduler.numDaemons == VM_Scheduler.numActiveThreads) && (VM.mainThread != null) && VM.mainThread.launched) { // no non-daemon thread remains and the main thread was launched terminateSystem = true; } if (terminateSystem) { if (systemShuttingDown == false) { systemShuttingDown = true; } else { terminateSystem = false; } } if (traceTermination) { VM.sysWriteln("VM_Thread.terminate: myThread.daemon = ", daemon); VM.sysWriteln(" VM_Scheduler.numActiveThreads = ", VM_Scheduler.numActiveThreads); VM.sysWriteln(" VM_Scheduler.numDaemons = ", VM_Scheduler.numDaemons); VM.sysWriteln(" terminateSystem = ", terminateSystem); } // end critical section // VM_Processor.getCurrentProcessor().enableThreadSwitching(); VM_Scheduler.threadCreationMutex.unlock(); if (VM.VerifyAssertions) { if (VM.fullyBooted || !terminateSystem) { VM_Processor.getCurrentProcessor().failIfThreadSwitchingDisabled(); } } if (terminateSystem) { if (uncaughtExceptionCount > 0) /* Use System.exit so that any shutdown hooks are run. */ { if (VM.TraceExceptionDelivery) { VM.sysWriteln("Calling sysExit due to uncaught exception."); } System.exit(VM.EXIT_STATUS_DYING_WITH_UNCAUGHT_EXCEPTION); } else if (thread instanceof VM_MainThread) { VM_MainThread mt = (VM_MainThread) thread; if (!mt.launched) { /* Use System.exit so that any shutdown hooks are run. It is * possible that shutdown hooks may be installed by static * initializers which were run by classes initialized before we * attempted to run the main thread. (As of this writing, 24 * January 2005, the Classpath libraries do not do such a thing, but * there is no reason why we should not support this.) This was * discussed on jikesrvm-researchers * on 23 Jan 2005 and 24 Jan 2005. */ System.exit(VM.EXIT_STATUS_MAIN_THREAD_COULD_NOT_LAUNCH); } } /* Use System.exit so that any shutdown hooks are run. */ System.exit(0); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } if (jniEnv != null) { VM_JNIEnvironment.deallocateEnvironment(jniEnv); jniEnv = null; } // release anybody waiting on this thread - // in particular, see {@link #join()} synchronized (this) { state = State.TERMINATED; notifyAll(this); } // become another thread // VM_Scheduler.releaseThreadSlot(threadSlot, this); beingDispatched = true; VM_Processor.getCurrentProcessor().dispatch(false); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } /** * Get the field that holds the cause of a thread death caused by a stop */ public final Throwable getCauseOfThreadDeath() { return causeOfThreadDeath; } /* * Support for yieldpoints */ /** * Yieldpoint taken in prologue. */ @BaselineSaveLSRegisters //Save all non-volatile registers in prologue @NoOptCompile //We should also have a pragma that saves all non-volatiles in opt compiler, // OSR_BaselineExecStateExtractor.java, should then restore all non-volatiles before stack replacement //todo fix this -- related to SaveVolatile @Entrypoint public static void yieldpointFromPrologue() { LocalAddress fp = VM_Magic.getFramePointer(); org.jikesrvm.scheduler.greenthreads.VM_GreenThread.yieldpoint(PROLOGUE, fp); } /** * Yieldpoint taken on backedge. */ @BaselineSaveLSRegisters //Save all non-volatile registers in prologue @NoOptCompile // We should also have a pragma that saves all non-volatiles in opt compiler, // OSR_BaselineExecStateExtractor.java, should then restore all non-volatiles before stack replacement // TODO fix this -- related to SaveVolatile @Entrypoint public static void yieldpointFromBackedge() { LocalAddress fp = VM_Magic.getFramePointer(); org.jikesrvm.scheduler.greenthreads.VM_GreenThread.yieldpoint(BACKEDGE, fp); } /** * Yieldpoint taken in epilogue. */ @BaselineSaveLSRegisters //Save all non-volatile registers in prologue @NoOptCompile //We should also have a pragma that saves all non-volatiles in opt compiler, // OSR_BaselineExecStateExtractor.java, should then restore all non-volatiles before stack replacement // TODO fix this -- related to SaveVolatile @Entrypoint public static void yieldpointFromEpilogue() { LocalAddress fp = VM_Magic.getFramePointer(); org.jikesrvm.scheduler.greenthreads.VM_GreenThread.yieldpoint(EPILOGUE, fp); } /* * Support for suspend/resume */ /** * Thread model dependent part of thread suspension */ protected abstract void suspendInternal(); /** * Suspend execution of current thread until it is resumed. * Call only if caller has appropriate security clearance. */ @LogicallyUninterruptible public final void suspend() { Throwable rethrow = null; changeThreadState(State.RUNNABLE, State.SUSPENDED); try { // let go of outer lock VM_ObjectModel.genericUnlock(thread); suspendInternal(); } catch (Throwable t) { rethrow = t; } // regain outer lock VM_ObjectModel.genericLock(thread); if (rethrow != null) { VM_Runtime.athrow(rethrow); } } /** * Thread model dependent part of thread resumption */ protected abstract void resumeInternal(); /** * Resume execution of a thread that has been suspended. * Call only if caller has appropriate security clearance. */ @Interruptible public final void resume() { changeThreadState(State.SUSPENDED, State.RUNNABLE); if (trace) VM_Scheduler.trace("VM_Thread", "resume() scheduleThread ", getIndex()); resumeInternal(); } /* * OSR support */ /** * Suspends the thread waiting for OSR (rescheduled by recompilation * thread when OSR is done). */ public final void osrSuspend() { changeThreadState(State.RUNNABLE, State.OSR_SUSPENDED); suspendInternal(); } /** * Suspends the thread waiting for OSR (rescheduled by recompilation * thread when OSR is done). */ public final void osrResume() { changeThreadState(State.OSR_SUSPENDED, State.RUNNABLE); if (trace) VM_Scheduler.trace("VM_Thread", "osrResume() scheduleThread ", getIndex()); resumeInternal(); } /* * Sleep support */ /** * Thread model dependent sleep * @param millis * @param ns */ @Interruptible protected abstract void sleepInternal(long millis, int ns) throws InterruptedException; /** * Suspend execution of current thread for specified number of seconds * (or fraction). */ @Interruptible public static void sleep(long millis, int ns) throws InterruptedException { VM_Thread myThread = VM_Scheduler.getCurrentThread(); myThread.changeThreadState(State.RUNNABLE, State.SLEEPING); myThread.sleepInternal(millis, ns); myThread.changeThreadState(State.SLEEPING, State.RUNNABLE); } /* * Wait and notify support */ /** * Support for Java {@link java.lang.Object#wait()} synchronization primitive. * * @param o the object synchronized on * @param millis the number of milliseconds to wait for notification */ @Interruptible protected abstract Throwable waitInternal(Object o, long millis); /** * Support for Java {@link java.lang.Object#wait()} synchronization primitive. * * @param o the object synchronized on */ @Interruptible protected abstract Throwable waitInternal(Object o); /** * Support for Java {@link java.lang.Object#wait()} synchronization primitive. * * @param o the object synchronized on */ @Interruptible /* only loses control at expected points -- I think -dave */ public static void wait(Object o) { if (STATS) waitOperations++; VM_Thread t = VM_Scheduler.getCurrentThread(); Throwable rethrow = t.waitInternal(o); if (rethrow != null) { VM_Runtime.athrow(rethrow); // doesn't return } } /** * Support for Java {@link java.lang.Object#wait()} synchronization primitive. * * @param o the object synchronized on * @param millis the number of milliseconds to wait for notification */ @LogicallyUninterruptible /* only loses control at expected points -- I think -dave */ public static void wait(Object o, long millis) { if (STATS) timedWaitOperations++; VM_Thread t = VM_Scheduler.getCurrentThread(); Throwable rethrow = t.waitInternal(o, millis); if (rethrow != null) { VM_Runtime.athrow(rethrow); } } /** * Support for Java {@link java.lang.Object#notify()} synchronization primitive. * * @param o the object synchronized on */ protected abstract void notifyInternal(Object o, VM_Lock l); /** * Support for Java {@link java.lang.Object#notifyAll()} synchronization primitive. * * @param o the object synchronized on */ protected abstract void notifyAllInternal(Object o, VM_Lock l); @LogicallyUninterruptible private static void raiseIllegalMonitorStateException(String msg, Object o) { throw new IllegalMonitorStateException(msg + o); } /** * Support for Java {@link java.lang.Object#notify()} synchronization primitive. * * @param o the object synchronized on */ public static void notify(Object o) { if (STATS) notifyOperations++; VM_Lock l = VM_ObjectModel.getHeavyLock(o, false); if (l == null) return; VM_Processor proc = VM_Processor.getCurrentProcessor(); if (l.getOwnerId() != proc.threadId) { raiseIllegalMonitorStateException("notifying", o); } VM_Scheduler.getCurrentThread().notifyInternal(o, l); } /** * Support for Java synchronization primitive. * * @param o the object synchronized on * @see java.lang.Object#notifyAll */ public static void notifyAll(Object o) { if (STATS) notifyAllOperations++; VM_Scheduler.LockModel l = (VM_Scheduler.LockModel)VM_ObjectModel.getHeavyLock(o, false); if (l == null) return; VM_Processor proc = VM_Processor.getCurrentProcessor(); if (l.getOwnerId() != proc.threadId) { raiseIllegalMonitorStateException("notifyAll", o); } VM_Scheduler.getCurrentThread().notifyAllInternal(o, l); } /* * Interrupt and stop support */ /** * Thread model dependent part of stopping/interrupting a thread */ protected abstract void killInternal(); /** * Deliver the throwable stopping/interrupting this thread */ @LogicallyUninterruptible public void kill(Throwable cause, boolean throwImmediately) { // yield() will notice the following and take appropriate action this.causeOfThreadDeath = cause; if (throwImmediately) { // FIXME - this is dangerous. Only called from Thread.stop(), // which is deprecated. this.throwInterruptWhenScheduled = true; } killInternal(); } /* * Park and unpark support */ /** * Park the current thread * @param isAbsolute is the time value given relative or absolute * @param time the timeout value in nanoseconds, 0 => no timeout */ @Interruptible public final void park(boolean isAbsolute, long time) throws Throwable { if (VM.VerifyAssertions) { VM_Thread curThread = VM_Scheduler.getCurrentThread(); VM._assert(curThread == this); } // Has the thread already been unparked? (ie the permit is available?) if (parkingPermit) { // Yes: exit early double checking illegal thread states changeThreadState(State.RUNNABLE, State.RUNNABLE); parkingPermit = false; } else if (throwInterruptWhenScheduled) { // Was this thread interrupted prior to parking? changeThreadState(State.RUNNABLE, State.RUNNABLE); if (causeOfThreadDeath == null) { // interrupt exceptions are swallowed so ignore } else { // Thread was stopped so throw thread death throw causeOfThreadDeath; } } else { // Put thread into parked state State parkedState; long millis = 0L; int ns = 0; // Do we have a timeout? if (time != 0) { parkedState = State.TIMED_PARK; millis = time / 1000000; if (isAbsolute) { // if it's an absolute amount of time then remove the current time as // we will adjust up by this much in the sleep millis -= (VM_Time.nanoTime() / ((long)1e6)); } ns = (int)time % 1000000; } else { parkedState = State.PARKED; } changeThreadState(State.RUNNABLE, parkedState); // Do we hold the lock on the surrounding java.lang.Thread? boolean holdsLock = holdsLock(thread); if (holdsLock) { // If someone locked the java.lang.Thread, release before going to sleep // to allow interruption VM_ObjectModel.genericUnlock(thread); } try { sleepInternal(millis, ns); } catch (InterruptedException thr) { // swallow thread interruptions } if (holdsLock) { VM_ObjectModel.genericLock(thread); } if (state != State.RUNNABLE) { // change thread to runnable unless already performed by athrow changeThreadState(parkedState, State.RUNNABLE); } } } /** * Unpark this thread, not necessarily the current thread */ public void unpark() { if (state == State.PARKED) { // Wake up sleeping thread kill(proxyInterruptException , false); } else if (state == State.RUNNABLE) { // Allow next call to park to just run through parkingPermit = true; } else { // nothing to do } } /** * Get this thread's index in {@link VM_Scheduler#threads}[]. */ @LogicallyUninterruptible public final int getIndex() { if (VM.VerifyAssertions) VM._assert((state == State.TERMINATED) || VM_Scheduler.threads[threadSlot] == this); return threadSlot; } /** * Get this thread's id for use in lock ownership tests. * This is just the thread's index as returned by {@link #getIndex()}, * shifted appropriately so it can be directly used in the ownership tests. */ public final int getLockingId() { if (VM.VerifyAssertions) VM._assert(VM_Scheduler.threads[threadSlot] == this); return threadSlot << VM_ThinLockConstants.TL_THREAD_ID_SHIFT; } /** * Change the size of the currently executing thread's stack. * @param newSize new size (in bytes) * @param exceptionRegisters register state at which stack overflow trap * was encountered (null --> normal method call, not a trap) */ @Interruptible public static void resizeCurrentStack(int newSize, VM_Registers exceptionRegisters) { if (traceAdjustments) VM.sysWrite("VM_Thread: resizeCurrentStack\n"); if (MM_Interface.gcInProgress()) { VM.sysFail("system error: resizing stack while GC is in progress"); } byte[] newStack = MM_Interface.newStack(newSize, false); VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for stack resizing"); transferExecutionToNewStack(newStack, exceptionRegisters); VM_Processor.getCurrentProcessor().enableThreadSwitching(); if (traceAdjustments) { VM_Thread t = VM_Scheduler.getCurrentThread(); VM.sysWrite("VM_Thread: resized stack ", t.getIndex()); VM.sysWrite(" to ", t.stack.length / 1024); VM.sysWrite("k\n"); } } @NoInline @BaselineNoRegisters //this method does not do a normal return and hence does not execute epilogue --> non-volatiles not restored! private static void transferExecutionToNewStack(byte[] newStack, VM_Registers exceptionRegisters) { // prevent opt compiler from inlining a method that contains a magic // (returnToNewStack) that it does not implement. VM_Thread myThread = VM_Scheduler.getCurrentThread(); byte[] myStack = myThread.stack; // initialize new stack with live portion of stack we're // currently running on // // lo-mem hi-mem // |<---myDepth----| // +----------+---------------+ // | empty | live | // +----------+---------------+ // ^myStack ^myFP ^myTop // // +-------------------+---------------+ // | empty | live | // +-------------------+---------------+ // ^newStack ^newFP ^newTop // LocalAddress myTop = VM_Magic.objectAsLocalAddress(myStack).plus(myStack.length); LocalAddress newTop = VM_Magic.objectAsLocalAddress(newStack).plus(newStack.length); LocalAddress myFP = VM_Magic.getFramePointer(); Offset myDepth = myTop.diff(myFP); LocalAddress newFP = newTop.minus(myDepth); // The frame pointer addresses the top of the frame on powerpc and // the bottom // on intel. if we copy the stack up to the current // frame pointer in here, the // copy will miss the header of the intel frame. Thus we make another // call // to force the copy. A more explicit way would be to up to the // frame pointer // and the header for intel. Offset delta = copyStack(newStack); // fix up registers and save areas so they refer // to "newStack" rather than "myStack" // if (exceptionRegisters != null) { adjustRegisters(exceptionRegisters, delta); } adjustStack(newStack, newFP, delta); // install new stack // myThread.stack = newStack; myThread.stackLimit = VM_Magic.objectAsLocalAddress(newStack).plus(STACK_SIZE_GUARD); VM_Processor.getCurrentProcessor().activeThreadStackLimit = myThread.stackLimit; // return to caller, resuming execution on new stack // (original stack now abandoned) // if (VM.BuildForPowerPC) { VM_Magic.returnToNewStack(VM_Magic.getCallerFramePointer(newFP)); } else if (VM.BuildForIA32) { VM_Magic.returnToNewStack(newFP); } if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } /** * This (suspended) thread's stack has been moved. * Fixup register and memory references to reflect its new position. * @param delta displacement to be applied to all interior references */ public final void fixupMovedStack(Offset delta) { if (traceAdjustments) VM.sysWrite("VM_Thread: fixupMovedStack\n"); if (!contextRegisters.getInnermostFramePointer().isZero()) { adjustRegisters(contextRegisters, delta); } if ((hardwareExceptionRegisters.inuse) && (hardwareExceptionRegisters.getInnermostFramePointer().NE(LocalAddress.zero()))) { adjustRegisters(hardwareExceptionRegisters, delta); } if (!contextRegisters.getInnermostFramePointer().isZero()) { adjustStack(stack, contextRegisters.getInnermostFramePointer(), delta); } stackLimit = stackLimit.plus(delta); } /** * A thread's stack has been moved or resized. * Adjust registers to reflect new position. * * @param registers registers to be adjusted * @param delta displacement to be applied */ private static void adjustRegisters(VM_Registers registers, Offset delta) { if (traceAdjustments) VM.sysWrite("VM_Thread: adjustRegisters\n"); // adjust FP // LocalAddress newFP = registers.getInnermostFramePointer().plus(delta); LocalAddress ip = registers.getInnermostInstructionAddress(); registers.setInnermost(ip, newFP); if (traceAdjustments) { VM.sysWrite(" fp="); VM.sysWrite(registers.getInnermostFramePointer()); } // additional architecture specific adjustments // (1) frames from all compilers on IA32 need to update ESP int compiledMethodId = VM_Magic.getCompiledMethodID(registers.getInnermostFramePointer()); if (compiledMethodId != INVISIBLE_METHOD_ID) { if (VM.BuildForIA32) { VM_Configuration.archHelper.adjustESP(registers, delta, traceAdjustments); } if (traceAdjustments) { VM_CompiledMethod compiledMethod = VM_CompiledMethods.getCompiledMethod(compiledMethodId); VM.sysWrite(" method="); VM.sysWrite(compiledMethod.getMethod()); VM.sysWrite("\n"); } } } /** * A thread's stack has been moved or resized. * Adjust internal pointers to reflect new position. * * @param stack stack to be adjusted * @param fp pointer to its innermost frame * @param delta displacement to be applied to all its interior references */ private static void adjustStack(byte[] stack, LocalAddress fp, Offset delta) { if (traceAdjustments) VM.sysWrite("VM_Thread: adjustStack\n"); while (VM_Magic.getCallerFramePointer(fp).NE(VM_Magic.addressAsLocalAddress(STACKFRAME_SENTINEL_FP))) { // adjust FP save area // VM_Magic.setCallerFramePointer(fp, VM_Magic.getCallerFramePointer(fp).plus(delta)); if (traceAdjustments) { VM.sysWrite(" fp=", fp.toWord()); } // advance to next frame // fp = VM_Magic.getCallerFramePointer(fp); } } /** * Initialize a new stack with the live portion of the stack * we're currently running on. * * <pre> * lo-mem hi-mem * |<---myDepth----| * +----------+---------------+ * | empty | live | * +----------+---------------+ * ^myStack ^myFP ^myTop * * +-------------------+---------------+ * | empty | live | * +-------------------+---------------+ * ^newStack ^newFP ^newTop * </pre> */ private static Offset copyStack(byte[] newStack) { VM_Thread myThread = VM_Scheduler.getCurrentThread(); byte[] myStack = myThread.stack; LocalAddress myTop = VM_Magic.objectAsLocalAddress(myStack).plus(myStack.length); LocalAddress newTop = VM_Magic.objectAsLocalAddress(newStack).plus(newStack.length); LocalAddress myFP = VM_Magic.getFramePointer(); Offset myDepth = myTop.diff(myFP); LocalAddress newFP = newTop.minus(myDepth); // before copying, make sure new stack isn't too small // if (VM.VerifyAssertions) { VM._assert(newFP.GE(VM_Magic.objectAsLocalAddress(newStack).plus(STACK_SIZE_GUARD))); } VM_Memory.memcopy(newFP, myFP, myDepth.toWord().toExtent()); return newFP.diff(myFP); } /** * Set the "isDaemon" status of this thread. * Although a java.lang.Thread can only have setDaemon invoked on it * before it is started, VM_Threads can become daemons at any time. * Note: making the last non daemon a daemon will terminate the VM. * * Note: This method might need to be uninterruptible so it is final, * which is why it isn't called setDaemon. * * Public so that java.lang.Thread can use it. */ public final void makeDaemon(boolean on) { if (daemon == on) { // nothing to do } else { daemon = on; if (state == State.NEW) { // thread will start as a daemon } else { VM_Scheduler.threadCreationMutex.lock("daemon creation mutex"); VM_Scheduler.numDaemons += on ? 1 : -1; VM_Scheduler.threadCreationMutex.unlock(); if (VM_Scheduler.numDaemons == VM_Scheduler.numActiveThreads) { if (VM.TraceThreads) { VM_Scheduler.trace("VM_Thread", "last non Daemon demonized"); } VM.sysExit(0); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } } } } /** * Dump information for all threads, via {@link VM#sysWrite(String)}. Each * thread's info is newline-terminated. * * @param verbosity Ignored. */ public static void dumpAll(int verbosity) { for (int i = 0; i < VM_Scheduler.threads.length; i++) { VM_Thread t = VM_Scheduler.threads[i]; if (t == null) continue; VM.sysWrite("Thread "); VM.sysWriteInt(i); VM.sysWrite(": "); VM.sysWriteHex(VM_Magic.objectAsAddress(t)); VM.sysWrite(" "); t.dump(verbosity); // Compensate for t.dump() not newline-terminating info. VM.sysWriteln(); } } /** * @return whether or not the thread has an active timer interval */ public final boolean hasActiveTimedInterval() { return timingDepth > 0; } /** * Begin a possibly nested timing interval. * @return the current value of {@link #totalNanos}. */ public final long startTimedInterval() { long now = VM_Time.nanoTime(); if (timingDepth == 0) { timingDepth = 1; startNano = now; totalNanos = 0; } else { timingDepth++; totalNanos += now - startNano; startNano = now; } return totalNanos; } /** * End a possibly nested timing interval */ public final long endTimedInterval() { long now = VM_Time.nanoTime(); timingDepth--; totalNanos += now - startNano; startNano = now; return totalNanos; } /** * Called from VM_Processor.dispatch when a thread is about to * start executing. */ public final void resumeInterval(long now) { if (VM.VerifyAssertions) VM._assert(startNano == 0); startNano = now; } /** * Called from {@link VM_Processor#dispatch} when a thread is about to stop * executing. */ public final void suspendInterval(long now) { totalNanos += now - startNano; startNano = 0; } /** @return The value of {@link #isBootThread} */ public final boolean isBootThread() { return this == bootThread; } /** @return Is this the MainThread ? */ private boolean isMainThread() { return thread instanceof VM_MainThread; } /** * Is this the Idle thread? * @return false */ public boolean isIdleThread() { return false; } /** * Is this the GC thread? * @return false */ public boolean isGCThread() { return false; } /** * Is this the debugger thread? * @return false */ public boolean isDebuggerThread() { return false; } /** Is this a system thread? */ public final boolean isSystemThread() { return systemThread; } /** Returns the value of {@link #daemon}. */ public final boolean isDaemonThread() { return daemon; } /** Is the thread started and not terminated */ public final boolean isAlive() { return (state != State.NEW) && (state != State.TERMINATED); } /** * Sets the name of the thread * @param name the new name for the thread * @see java.lang.Thread#setName(String) */ public final void setName(String name) { this.name = name; } /** * Gets the name of the thread * @see java.lang.Thread#getName() */ public final String getName() { return name; } /** * Does the currently running Thread hold the lock on an obj? * @param obj the object to check * @return whether the thread holds the lock * @see java.lang.Thread#holdsLock(Object) */ public final boolean holdsLock(Object obj) { VM_Thread mine = VM_Scheduler.getCurrentThread(); return VM_ObjectModel.holdsLock(obj, mine); } /** * Throw the external interrupt associated with the thread now it is running */ @LogicallyUninterruptible protected final void postExternalInterrupt() { throwInterruptWhenScheduled = false; Throwable t = causeOfThreadDeath; causeOfThreadDeath = null; if (t instanceof InterruptedException && t != proxyInterruptException) { t.fillInStackTrace(); } state = State.RUNNABLE; VM_Runtime.athrow(t); } /** * Was this thread interrupted? * @return whether the thread has been interrupted * @see java.lang.Thread#isInterrupted() */ public final boolean isInterrupted() { return interrupted; } /** * Clear the interrupted status of this thread * @see java.lang.Thread#interrupted() */ public final void clearInterrupted() { interrupted = false; } /** * Interrupt this thread * @see java.lang.Thread#interrupt() */ @Interruptible public final void interrupt() { interrupted = true; switch (state) { case WAITING: case TIMED_WAITING: kill(new InterruptedException("wait interrupted"), false); break; case SLEEPING: kill(new InterruptedException("sleep interrupted"), false); break; case JOINING: kill(new InterruptedException("join interrupted"), false); break; case PARKED: case TIMED_PARK: kill(proxyInterruptException, false); break; default: kill(new InterruptedException(), false); } } /** * Get the priority of the thread * @return the thread's priority * @see java.lang.Thread#getPriority() */ public final int getPriority() { return priority; } /** * Set the priority of the thread * @param priority * @see java.lang.Thread#getPriority() */ public final void setPriority(int priority) { this.priority = priority; } /** * Get the state of the thread in a manner compatible with the Java API * @return thread state * @see java.lang.Thread#getState() */ @Interruptible public final Thread.State getState() { switch (state) { case NEW: return Thread.State.NEW; case RUNNABLE: return Thread.State.RUNNABLE; case BLOCKED: return Thread.State.BLOCKED; case WAITING: case SUSPENDED: case OSR_SUSPENDED: case JOINING: case PARKED: case IO_WAITING: case PROCESS_WAITING: return Thread.State.WAITING; case TIMED_WAITING: case TIMED_PARK: case SLEEPING: return Thread.State.TIMED_WAITING; case TERMINATED: return Thread.State.TERMINATED; } VM.sysFail("Unknown thread state " + state); return null; } /** * Wait for the thread to die or for the timeout to occur * @param ms milliseconds to wait * @param ns nanoseconds to wait */ @Interruptible public final void join(long ms, int ns) throws InterruptedException { VM_Thread myThread = VM_Scheduler.getCurrentThread(); if (VM.VerifyAssertions) VM._assert(myThread != this); synchronized(this) { myThread.changeThreadState(State.RUNNABLE, State.JOINING); if (ms == 0 && ns != 0) { ms++; } if (ms == 0) { while (isAlive()) { wait(this); } } else { long startNano = VM_Time.nanoTime(); long timeLeft; if (isAlive()) { do { long elapsedMillis = (VM_Time.nanoTime()-startNano)/((long) 1e6); timeLeft = ms - elapsedMillis; wait(this, timeLeft); } while (isAlive() && timeLeft > 0); } } myThread.changeThreadState(State.JOINING, State.RUNNABLE); } } /** * Count the stack frames of this thread */ @Interruptible public final int countStackFrames() { if (state != State.SUSPENDED) { throw new IllegalThreadStateException("Thread.countStackFrames called on non-suspended thread"); } throw new VM_UnimplementedError(); } /** * @return the length of the stack */ public final int getStackLength() { return stack.length; } /** * @return the stack */ public final byte[] getStack() { return stack; } /** * @return the hardware exception registers */ public final VM_Registers getHardwareExceptionRegisters() { return hardwareExceptionRegisters; } /** * @return the hardware exception registers */ public final VM_Registers getContextRegisters() { return contextRegisters; } /** * Give a string of information on how a thread is set to be scheduled */ @Interruptible public abstract String getThreadState(); /** Set the initial attempt. */ public final void reportCollectionAttempt() { collectionAttempt++; } /** Set the initial attempt. */ public final int getCollectionAttempt() { return collectionAttempt; } /** Resets the attempts. */ public final void resetCollectionAttempts() { collectionAttempt = 0; } /** Get the physical allocation failed flag. */ public final boolean physicalAllocationFailed() { return physicalAllocationFailed; } /** Set the physical allocation failed flag. */ public final void setPhysicalAllocationFailed() { physicalAllocationFailed = true; } /** Clear the physical allocation failed flag. */ public final void clearPhysicalAllocationFailed() { physicalAllocationFailed = false; } /** Set the emergency allocation flag. */ public final void setEmergencyAllocation() { emergencyAllocation = true; } /** Clear the emergency allocation flag. */ public final void clearEmergencyAllocation() { emergencyAllocation = false; } /** Read the emergency allocation flag. */ public final boolean emergencyAllocation() { return emergencyAllocation; } /** * Returns the outstanding OutOfMemoryError. */ public final OutOfMemoryError getOutOfMemoryError() { return outOfMemoryError; } /** * Sets the outstanding OutOfMemoryError. */ public final void setOutOfMemoryError(OutOfMemoryError oome) { outOfMemoryError = oome; } /** * Get the thread to use for building stack traces. * NB overridden by {@link org.jikesrvm.memorymanagers.mminterface.VM_CollectorThread} */ @Uninterruptible public VM_Thread getThreadForStackTrace() { return this; } /** * Clears the outstanding OutOfMemoryError. */ public final void clearOutOfMemoryError() { /* * SEE RVM-141 * To avoid problems in GCTrace configuration, only clear the OOM if it is non-NULL. */ if (outOfMemoryError != null) { outOfMemoryError = null; } } @Interruptible public final void handleUncaughtException(Throwable exceptionObject) { uncaughtExceptionCount++; if (exceptionObject instanceof OutOfMemoryError) { /* Say allocation from this thread is emergency allocation */ setEmergencyAllocation(); } handlePossibleRecursiveException(); VM.enableGC(); if (thread == null) { VM.sysWrite("Exception in the primordial thread \"", toString(), "\" while booting: "); } else { // This is output like that of the Sun JDK. VM.sysWrite("Exception in thread \"", getName(), "\": "); } if (VM.fullyBooted) { exceptionObject.printStackTrace(); } VM_Scheduler.getCurrentThread().terminate(); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } /** Handle the case of exception handling triggering new exceptions. */ private void handlePossibleRecursiveException() { if (uncaughtExceptionCount > 1 && uncaughtExceptionCount <= VM.maxSystemTroubleRecursionDepth + VM.maxSystemTroubleRecursionDepthBeforeWeStopVMSysWrite) { VM.sysWrite("We got an uncaught exception while (recursively) handling "); VM.sysWrite(uncaughtExceptionCount - 1); VM.sysWrite(" uncaught exception"); if (uncaughtExceptionCount - 1 != 1) { VM.sysWrite("s"); } VM.sysWriteln("."); } if (uncaughtExceptionCount > VM.maxSystemTroubleRecursionDepth) { VM.dieAbruptlyRecursiveSystemTrouble(); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } } /** * Dump this thread's identifying information, for debugging, via * {@link VM#sysWrite(String)}. * We do not use any spacing or newline characters. Callers are responsible * for space-separating or newline-terminating output. */ public void dump() { dump(0); } /** * Dump this thread's identifying information, for debugging, via * {@link VM#sysWrite(String)}. * We pad to a minimum of leftJustify characters. We do not use any spacing * characters. Callers are responsible for space-separating or * newline-terminating output. * * @param leftJustify minium number of characters emitted, with any * extra characters being spaces. */ public void dumpWithPadding(int leftJustify) { char[] buf = VM_Services.grabDumpBuffer(); int len = dump(buf); VM.sysWrite(buf, len); for (int i = leftJustify - len; i > 0; i--) { VM.sysWrite(" "); } VM_Services.releaseDumpBuffer(); } /** * Dump this thread's identifying information, for debugging, via * {@link VM#sysWrite(String)}. * We do not use any spacing or newline characters. Callers are responsible * for space-separating or newline-terminating output. * * This function avoids write barriers and allocation. * * @param verbosity Ignored. */ public void dump(int verbosity) { char[] buf = VM_Services.grabDumpBuffer(); int len = dump(buf); VM.sysWrite(buf, len); VM_Services.releaseDumpBuffer(); } /** * Dump this thread's info, for debugging. * Copy the info about it into a destination char * array. We do not use any spacing or newline characters. * * This function may be called during GC; it avoids write barriers and * allocation. * * For this reason, we do not throw an * <code>IndexOutOfBoundsException</code>. * * @param dest char array to copy the source info into. * @param offset Offset into <code>dest</code> where we start copying * * @return 1 plus the index of the last character written. If we were to * write zero characters (which we won't) then we would return * <code>offset</code>. This is intended to represent the first * unused position in the array <code>dest</code>. However, it also * serves as a pseudo-overflow check: It may have the value * <code>dest.length</code>, if the array <code>dest</code> was * completely filled by the call, or it may have a value greater * than <code>dest.length</code>, if the info needs more than * <code>dest.length - offset</code> characters of space. * * -1 if <code>offset</code> is negative. */ public int dump(char[] dest, int offset) { offset = VM_Services.sprintf(dest, offset, getIndex()); // id if (daemon) { offset = VM_Services.sprintf(dest, offset, "-daemon"); // daemon thread? } if (isBootThread()) { offset = VM_Services.sprintf(dest, offset, "-Boot"); // Boot (Primordial) thread } if (isMainThread()) { offset = VM_Services.sprintf(dest, offset, "-main"); // Main Thread } if (isIdleThread()) { offset = VM_Services.sprintf(dest, offset, "-idle"); // idle thread? } if (isGCThread()) { offset = VM_Services.sprintf(dest, offset, "-collector"); // gc thread? } if (beingDispatched) { offset = VM_Services.sprintf(dest, offset, "-being_dispatched"); } offset = VM_Services.sprintf(dest, offset, "-"); offset = VM_Services.sprintf(dest, offset, java.lang.JikesRVMSupport.getEnumName(state)); if (state == State.TIMED_WAITING || state == State.TIMED_PARK) { offset = VM_Services.sprintf(dest, offset, "("); long timeLeft = wakeupNanoTime - VM_Time.nanoTime(); offset = VM_Services.sprintf(dest, offset, (long)VM_Time.nanosToMillis(timeLeft)); offset = VM_Services.sprintf(dest, offset, "ms)"); } if (throwInterruptWhenScheduled) { offset = VM_Services.sprintf(dest, offset, "-interrupted"); } return offset; } /** * Dump this thread's info, for debugging. * Copy the info about it into a destination char * array. We do not use any spacing or newline characters. * * This is identical to calling {@link #dump(char[],int)} with an * <code>offset</code> of zero. */ public int dump(char[] dest) { return dump(dest, 0); } /** Dump statistics gather on operations */ static void dumpStats() { VM.sysWrite("FatLocks: "); VM.sysWrite(waitOperations); VM.sysWrite(" wait operations\n"); VM.sysWrite("FatLocks: "); VM.sysWrite(timedWaitOperations); VM.sysWrite(" timed wait operations\n"); VM.sysWrite("FatLocks: "); VM.sysWrite(notifyOperations); VM.sysWrite(" notify operations\n"); VM.sysWrite("FatLocks: "); VM.sysWrite(notifyAllOperations); } }