/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.scheduler;
import org.jnode.annotation.Inline;
import org.jnode.annotation.Internal;
import org.jnode.annotation.KernelSpace;
import org.jnode.annotation.LoadStatics;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.PrivilegedActionPragma;
import org.jnode.annotation.SharedStatics;
import org.jnode.annotation.Uninterruptible;
import org.jnode.util.NumberUtils;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VmAccessControlContext;
import org.jnode.vm.VmAccessController;
import org.jnode.vm.VmImpl;
import org.jnode.vm.VmMagic;
import org.jnode.vm.VmStackFrame;
import org.jnode.vm.VmStackReader;
import org.jnode.vm.VmSystem;
import org.jnode.vm.classmgr.ObjectFlags;
import org.jnode.vm.classmgr.VmIsolatedStatics;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.ObjectVisitor;
import org.jnode.vm.facade.VmHeapManager;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.isolate.VmIsolate;
import org.jnode.vm.objects.VmSystemObject;
import org.vmmagic.pragma.UninterruptiblePragma;
import org.vmmagic.unboxed.Address;
/**
* VM thread implementation
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@SharedStatics
@MagicPermission
public abstract class VmThread extends VmSystemObject implements org.jnode.vm.facade.VmThread {
/**
* If the stackpointer grows to this distance from the size of the stack, a
* stack overflow exception is raised.
*/
public static final int STACK_OVERFLOW_LIMIT_SLOTS = 256;
public static final int DEFAULT_STACK_SLOTS = 16 * 1024;
public static final int STACKTRACE_LIMIT = 256;
public static final int EX_NULLPOINTER = 0;
public static final int EX_PAGEFAULT = 1;
public static final int EX_INDEXOUTOFBOUNDS = 2;
public static final int EX_DIV0 = 3;
public static final int EX_ABSTRACTMETHOD = 4;
public static final int EX_STACKOVERFLOW = 5;
// public static final int EX_CLASSCAST = 6;
public static final int EX_COPRO_OR = 7;
public static final int EX_COPRO_ERR = 8;
/**
* A link to my java.lang.Thread
*/
private Thread javaThread;
/**
* Next pointer used in queues
*/
protected final VmThreadQueueEntry queueEntry = new VmThreadQueueEntry(this);
/**
* Next pointer used in list of sleeping threads
*/
protected final VmThreadQueueEntry sleepQueueEntry = new VmThreadQueueEntry(
this);
/**
* Next pointer used in list of all threads
*/
protected final VmThreadQueueEntry allThreadsEntry = new VmThreadQueueEntry(
this);
/**
* The size (in bytes) of the stack of this thread
*/
private int stackSize;
/**
* The pointer to the stack
*/
private Object stack;
/**
* The pointer to end of the stack (used by native code)
*/
protected volatile Address stackEnd;
/**
* Has this thread had a stackoverflow?
*/
private boolean stackOverflow;
/**
* The current state of this thread
*/
private int threadState = CREATED;
/**
* When to wakeup (if sleeping)
*/
protected long wakeupTime;
/**
* The monitor i'm waiting for
*/
private Monitor waitForMonitor;
/**
* The most recently owned monitor.
*/
private Monitor lastOwnedMonitor;
/**
* My priority
*/
protected int priority = Thread.NORM_PRIORITY;
/**
* Identifier of this thread
*/
private final int id;
/**
* Identifier of the last created thread
*/
private static int lastId;
/**
* Has this thread been interrupted?
*/
private boolean interrupted;
/**
* Is this thread in an exception initialization?
*/
boolean inException;
/**
* Is this thread in the process of being stopped?
*/
private boolean stopping;
/**
* Name of this thread.
*/
private String name;
/**
* Inherited context of this thread
*/
private VmAccessControlContext context;
private boolean inSystemException;
/**
* The isolated statics table of the isolate that this thread is currently
* running is
*/
private volatile VmIsolatedStatics isolatedStatics;
/**
* The processor that is required to run this thread. Null means no specific
* requirements
*/
private volatile VmProcessor requiredProcessor;
/**
* The processor currently at work on this thread
*/
volatile VmProcessor currentProcessor;
/**
* State is set to CREATED by the static initializer. Once set to other than
* CREATED, it should never go back. Alternates between RUNNING and
* SUSPENDED/WAITING as suspend()/wait() and reseume() are called.
* <p/>
* Can be set to INTERRUPTED if ASLEEP, or WAITING. Can be set to DESTROYED
* at any time. Can be set to STOPPED at any time.
* <p/>
* If a Thread blocks in a wait(), its state stays as RUNNING. This might
* need to change.
* <p/>
* Can sleep if RUNNING. <blockquote>
* <p/>
* <pre>
* /--------------------\ V | CREATED -> RUNNING -> SUSPENDED /
* </blockquote>
* </pre>
*/
static final int CREATED = 0;
static final int ASLEEP = 3;
static final int DESTROYED = 5;
static final int RUNNING = 1;
static final int STOPPED = 4;
static final int SUSPENDED = 2;
static final int WAITING_ENTER = 6;
static final int WAITING_NOTIFY = 7;
static final int WAITING_NOTIFY_TIMEOUT = 8;
static final int YIELDING = 9;
static final int MAXSTATE = YIELDING;
static final String[] STATE_NAMES = {"CREATED", "RUNNING", "SUSPENDED",
"ASLEEP", "STOPPED", "DESTROYED", "WAITING_ENTER",
"WAITING_NOTIFY", "WAITING_NOTIFY_TIMEOUT", "YIELDING"};
/**
* Create a new instance. This constructor can only be called during the
* bootstrap phase.
*/
protected VmThread(VmIsolatedStatics isolatedStatics, int slotSize) {
this.threadState = RUNNING;
this.stackSize = DEFAULT_STACK_SLOTS * slotSize;
this.id = (1 << ObjectFlags.THREAD_ID_SHIFT);
MonitorManager.testThreadId(this.id);
this.isolatedStatics = isolatedStatics;
getScheduler().registerThread(this);
}
/**
* Create a new instance. This constructor can only be called during the
* bootstrap phase.
*/
protected VmThread(VmIsolatedStatics isolatedStatics, Object stack,
Address stackEnd, int stackSize) {
this.isolatedStatics = isolatedStatics;
this.threadState = RUNNING;
this.stackSize = stackSize;
this.stack = stack;
this.stackEnd = stackEnd;
this.id = createId();
MonitorManager.testThreadId(this.id);
}
/**
* Create a new instance.
*
* @param javaThread
*/
public VmThread(VmIsolatedStatics isolatedStatics, Thread javaThread) {
this.isolatedStatics = isolatedStatics;
this.javaThread = javaThread;
this.threadState = CREATED;
this.stackSize = DEFAULT_STACK_SLOTS * VmUtils.getVm().getArch().getReferenceSize();
this.id = createId();
MonitorManager.testThreadId(this.id);
this.context = VmAccessController.getContext();
}
/**
* Return the current thread
*
* @return The current thread
*/
public static VmThread currentThread() {
return VmMagic.currentProcessor().getCurrentThread();
}
/**
* Initialize the threading system.
*/
@Internal
public static void initialize() {
// Ensure that we have a java.lang.Thread object for the root thread.
final VmThread currentThread = currentThread();
currentThread.asThread();
lastId = currentThread.id;
}
/**
* Count the number of stackframes in this thread.
*
* @return int
*/
public final int countStackFrames() {
final VmProcessor proc = VmProcessor.current();
final VmStackReader reader = proc.getArchitecture().getStackReader();
return reader.countStackFrames(VmMagic.getCurrentFrame());
}
/**
* {@inheritDoc}
*/
public final Thread asThread() {
if (javaThread == null) {
javaThread = new Thread(this);
}
return javaThread;
}
/**
* Has this object already a reference to a java.lang.Thread object?
*
* @return boolean
*/
public final boolean hasJavaThread() {
return (javaThread != null);
}
final void checkAccess() {
asThread().checkAccess();
}
public final void start() {
switch (threadState) {
case CREATED: {
// Screen.debug("thread.start");
final VmScheduler scheduler = VmMagic.currentProcessor()
.getScheduler();
stack = VmSystem.allocStack(stackSize);
Unsafe.initThread(this, stack, stackSize);
stackEnd = getStackEnd(stack, stackSize);
scheduler.registerThread(this);
threadState = RUNNING;
scheduler.addToReadyQueue(this, false, "thread.start");
break;
}
case RUNNING:
case SUSPENDED:
case WAITING_ENTER:
case WAITING_NOTIFY:
case WAITING_NOTIFY_TIMEOUT:
case ASLEEP:
throw new IllegalThreadStateException("already started");
case STOPPED:
/* XXX */
break;
case DESTROYED:
throw new IllegalThreadStateException("destroyed");
default:
throw new IllegalThreadStateException("Unknown thread state");
}
}
/**
* Stop the thread permanently.
*
* @param ex
* @throws UninterruptiblePragma
*/
public final void stop(Throwable ex) throws UninterruptiblePragma {
this.stopping = true;
if (javaThread != null) {
javaThread.onExit();
//exit the current isolate if needed
if (ex instanceof ThreadDeath) {
VmIsolate.currentIsolate().implicitExit(this, 0);
} else {
VmIsolate.currentIsolate().uncaughtExceptionExit();
}
// Notify joining threads
synchronized (javaThread) {
javaThread.notifyAll();
}
}
// Do the low level stop uninterrupted
doStop();
}
/**
* Stop the thread permanently.
*
* @param ex
* @throws UninterruptiblePragma
*/
public final void stopForced(Throwable ex) throws UninterruptiblePragma {
this.stopping = true;
if (javaThread != null) {
javaThread.onExit();
// Notify joining threads
synchronized (javaThread) {
javaThread.notifyAll();
}
}
// Do the low level stop uninterrupted
doStop();
}
/**
* Stop the thread permanently.
*
* @param ex
* @throws UninterruptiblePragma
*/
@Uninterruptible
private final void doStop() {
//release monitors
Monitor lom = lastOwnedMonitor;
while (lom != null) {
Monitor prev = lom.getPrevious();
lom.release(this);
if (prev == lom)
break;
lom = prev;
}
lastOwnedMonitor = null;
final VmProcessor proc = VmMagic.currentProcessor();
final VmThread current = proc.getCurrentThread();
proc.getScheduler().unregisterThread(this);
// Go into low level stuff
proc.disableReschedule(true);
this.threadState = STOPPED;
if (current == this) {
proc.suspend(true);
} else {
proc.enableReschedule(true);
}
}
/**
* Destroys this thread, without any cleanup. Any monitors it has locked
* remain locked. (This method is not implemented.)
*/
public final void destroy() {
}
/**
* Interrupt this thread.
*
* @throws UninterruptiblePragma
*/
@Uninterruptible
public final void interrupt() {
// Set interrupted state
this.interrupted = true;
// Add to scheduler queue
final VmProcessor proc = VmMagic.currentProcessor();
proc.disableReschedule(true);
try {
switch (threadState) {
case ASLEEP:
case WAITING_ENTER:
case WAITING_NOTIFY:
case WAITING_NOTIFY_TIMEOUT: {
// Remove from queues
wakeUpByScheduler();
// Add to ready queue
proc.getScheduler().addToReadyQueue(this, false, "thread.interrupt");
break;
}
}
} finally {
proc.enableReschedule(true);
}
}
/**
* Test the interruption status. If interrupted, the interrupted status is
* cleared and an InterruptedException is thrown, otherwise this method
* returns without any change in state.
*
* @throws UninterruptiblePragma
* @throws InterruptedException
*/
final void testAndClearInterruptStatus() throws UninterruptiblePragma,
InterruptedException {
final boolean throwIE = this.interrupted;
if (throwIE) {
this.interrupted = false;
throw new InterruptedException();
}
}
/**
* Resume this thread.
*/
public final void resume() {
checkAccess();
if (threadState != SUSPENDED) {
throw new IllegalThreadStateException("Not suspended");
} else {
final VmProcessor proc = VmProcessor.current();
threadState = RUNNING;
// FIXME make multi cpu safe
proc.getScheduler().addToReadyQueue(this, false, "thread.resume");
}
}
/**
* Set the state from suspended to yielding
*/
@KernelSpace
@Uninterruptible
final void unsecureResume() {
final VmProcessor proc = VmMagic.currentProcessor();
if (threadState == SUSPENDED) {
threadState = RUNNING;
// FIXME make multi cpu safe
proc.getScheduler().addToReadyQueue(this, false,
"thread.unsecureResume");
}
}
/**
* Suspend this thread.
*
* @throws UninterruptiblePragma
*/
@Uninterruptible
final void unsecureSuspend() {
if (threadState != RUNNING) {
throw new IllegalThreadStateException("Not running");
} else {
final VmProcessor proc = VmMagic.currentProcessor();
proc.disableReschedule(true);
this.threadState = SUSPENDED;
proc.suspend(true);
}
}
/**
* Suspend this thread.
*
* @throws UninterruptiblePragma
*/
@Uninterruptible
public final void suspend() {
checkAccess();
unsecureSuspend();
}
/**
* Give up the CPU.
*/
@Inline
public static void yield() {
VmProcessor.current().yield(false);
}
/**
* Set the state to YIELDING.
*
* @throws UninterruptiblePragma
*/
final void setYieldingState() throws UninterruptiblePragma {
if (threadState == RUNNING) {
threadState = YIELDING;
}
}
/**
* Go to sleep for the given period.
*
* @param millis
* @param nanos
* @throws InterruptedException
* @throws UninterruptiblePragma
*/
@Uninterruptible
public final void sleep(long millis, int nanos) throws InterruptedException {
if (currentThread() != this) {
return;
}
if (threadState != RUNNING) {
return;
}
// Test interrupted status
if (this.interrupted) {
this.interrupted = false;
throw new InterruptedException();
}
final long wakeupTime = VmSystem.currentKernelMillis() + millis;
final VmProcessor proc = VmProcessor.current();
proc.disableReschedule(true);
this.wakeupTime = wakeupTime;
this.threadState = ASLEEP;
proc.getScheduler().addToSleepQueue(this);
/* Now un-schedule myself */
proc.suspend(true);
/* We're back alive */
testAndClearInterruptStatus();
}
/**
* Returns <code>true</code> if the thread represented by this object is
* running (including suspended, asleep, or interrupted). Returns
* <code>false</code> if the thread hasn't be started yet, is stopped or
* destroyed.
*
* @return <code>true</code> if thread is alive, <code>false</code> if
* not.
* @see #start()
* @see #stop(Throwable)
* @see #suspend()
* @see #interrupt()
*/
public final boolean isAlive() {
switch (threadState) {
case CREATED:
return false;
case RUNNING:
case SUSPENDED:
case WAITING_ENTER:
case WAITING_NOTIFY:
case WAITING_NOTIFY_TIMEOUT:
case ASLEEP:
case YIELDING:
return !stopping;
case STOPPED:
case DESTROYED:
return false;
default:
throw new RuntimeException("reality failure");
}
}
/**
* Is this thread in the running state?
*
* @return boolean
* @throws UninterruptiblePragma
*/
@KernelSpace
@Uninterruptible
public final boolean isRunning() {
return (threadState == RUNNING);
}
/**
* Is this thread in the yielding state?
*
* @return boolean
* @throws UninterruptiblePragma
*/
@KernelSpace
@Uninterruptible
public final boolean isYielding() {
return (threadState == YIELDING);
}
/**
* Is this thread in the process of being stopped?
*
* @return boolean
*/
public final boolean isStopping() {
return stopping;
}
/**
* Has this thread been interrupted?
*
* @return boolean
*/
public final boolean isInterrupted(boolean clearFlag) {
boolean result = this.interrupted;
if (clearFlag) {
this.interrupted = false;
}
return result;
}
/**
* Is this thread waiting in a monitor?
*
* @return boolean
*/
@KernelSpace
@Uninterruptible
public final boolean isWaiting() {
return ((threadState >= WAITING_ENTER) && (threadState <= WAITING_NOTIFY_TIMEOUT));
}
/**
* Gets the state of this thread.
*
* @return the thread state value
*/
@KernelSpace
final int getThreadState() {
return threadState;
}
/**
* Gets the thread this thread is waiting for (or null).
*
* @return the VmThread for the thread that this one is
* waiting for, or {@code null}.
*/
@KernelSpace
final VmThread getWaitForThread() {
Monitor m = this.waitForMonitor;
return (m != null) ? m.getOwner() : null;
}
/**
* Gets a human readable name for the current thread state.
*
* @return the thread state name
*/
@KernelSpace
@Uninterruptible
public final String getThreadStateName() {
return STATE_NAMES[threadState];
}
/**
* Call the run method of the (java version of) the given thread and when
* run returns, kill the given thread. This method is called by the native
* code that is setup by Unsafe.initThread.
*
* @param thread
*/
@LoadStatics
protected static final void runThread(VmThread thread) {
Throwable t = null;
try {
thread.asThread().run();
} catch (Throwable ex) {
try {
t = ex;
ex.printStackTrace();
} catch (Throwable ex2) {
/* Ignore */
}
} finally {
try {
if (t == null)
thread.stop(new ThreadDeath());
else
thread.stop(t);
} catch (Throwable ex) {
/* Ignore */
while (true) {
Unsafe.idle();
}
}
}
}
/**
* Is it already time for me to wakeup?
*
* @param curTime
* @return boolean
* @throws UninterruptiblePragma
*/
@KernelSpace
@Uninterruptible
final boolean canWakeup(long curTime) {
return (curTime >= wakeupTime);
}
/**
* Setup this thread to wait for the given monitor
*
* @param monitor
* @param One of the Thread WAITING_XYZ states.
*/
@Uninterruptible
final void prepareWait(Monitor monitor, int waitState) {
// Keep this order of assignments!
this.waitForMonitor = monitor;
this.threadState = waitState;
}
/**
* Wake this thread up after being locked in the given monitor.
*
* @param monitor
* @throws UninterruptiblePragma
*/
@Uninterruptible
final void wakeupAfterMonitor(Monitor monitor) {
if (isWaiting()) {
final VmProcessor proc = VmMagic.currentProcessor();
proc.disableReschedule(true);
try {
this.threadState = RUNNING;
proc.getScheduler().addToReadyQueue(this, false,
"thread.wakeupAfterMonitor");
} finally {
proc.enableReschedule(true);
}
} else {
Unsafe.debug("Oops thread was not waiting? threadState="
+ threadState);
}
}
/**
* This thread is selected as new thread by the scheduler. Set the thread
* state to running.
*
* @throws UninterruptiblePragma
*/
@KernelSpace
@Uninterruptible
final void wakeUpByScheduler() {
switch (threadState) {
case ASLEEP:
case RUNNING:
case YIELDING: {
// Do nothing
break;
}
case WAITING_ENTER:
case WAITING_NOTIFY:
case WAITING_NOTIFY_TIMEOUT: {
final Monitor mon = this.waitForMonitor;
mon.removeThreadFromQueues(this);
break;
}
default: {
Unsafe.debug("Incorrect threadState in wakeUpByScheduler ");
Unsafe.debug(threadState);
}
}
threadState = RUNNING;
}
/**
* @return The thread's priority.
*/
public final int getPriority() {
return priority;
}
/**
* Sets the thread's priority.
*
* @param priority The priority to set
*/
public void setPriority(int priority) {
checkAccess();
if ((priority < Thread.MIN_PRIORITY)
|| (priority > Thread.MAX_PRIORITY)) {
throw new IllegalArgumentException("Invalid priority");
}
if (asThread() instanceof SystemThread) {
this.priority = priority;
}
}
/**
* Gets the stack of this thread
*
* @return The stack
*/
protected final Object getStack() {
return stack;
}
/**
* Gets the size of the stack (of this thread) in bytes.
*
* @return The stack size
*/
public int getStackSize() {
return stackSize;
}
/**
* Create a new (unique) thread identifier.
*
* @return The id
*/
private static synchronized int createId() {
if (lastId == 0) {
lastId = 8;
}
final int id = ++lastId;
return id << ObjectFlags.THREAD_ID_SHIFT;
}
/**
* Gets the identifier of this thread. This identifier has already been
* shifted by THREAD_ID_SHIFT.
*
* @return The id
* @see ObjectFlags#THREAD_ID_SHIFT
*/
@KernelSpace
@Uninterruptible
public final int getId() {
return id;
}
/**
* Gets the name of this thread.
*
* @return the name of this thread.
*/
@KernelSpace
public final String getName() {
return name;
}
/**
* Update my name from the java thread that wraps me.
*/
public final void updateName() {
if (this.javaThread != null) {
this.name = javaThread.getName();
}
}
/**
* @return {@code true} if the stack has overflowed.
*/
boolean isStackOverflow() {
return this.stackOverflow;
}
/**
* Verify the state of this thread.
*
* @throws UninterruptiblePragma
*/
final void verifyState() throws UninterruptiblePragma {
switch (threadState) {
case CREATED:
if (queueEntry.isInUse()) {
throw new Error(
"Created thread cannot have an inuse queueEntry");
}
if (sleepQueueEntry.isInUse()) {
throw new Error(
"Created thread cannot have an inuse sleepQueueEntry");
}
break;
case ASLEEP:
if (queueEntry.isInUse()) {
throw new Error(
"Sleeping thread cannot have an inuse queueEntry");
}
if (!sleepQueueEntry.isInUse()) {
throw new Error(
"Sleeping thread must have an inuse sleepQueueEntry");
}
break;
case DESTROYED:
if (queueEntry.isInUse()) {
throw new Error(
"Destroyed thread cannot have an inuse queueEntry");
}
if (sleepQueueEntry.isInUse()) {
throw new Error(
"Destroyed thread cannot have an inuse sleepQueueEntry");
}
break;
case RUNNING:
if (!queueEntry.isInUse()) {
if (VmProcessor.current().getCurrentThread() != this) {
throw new Error(
"Running thread must be inuse on ready queue or current thread");
}
}
if (sleepQueueEntry.isInUse()) {
throw new Error(
"Running thread cannot have an inuse sleepQueueEntry");
}
break;
case STOPPED:
if (queueEntry.isInUse()) {
throw new Error(
"Stopped thread cannot have an inuse queueEntry");
}
if (sleepQueueEntry.isInUse()) {
throw new Error(
"Stopped thread cannot have an inuse sleepQueueEntry");
}
break;
case SUSPENDED:
if (queueEntry.isInUse()) {
throw new Error(
"Suspended thread cannot have an inuse queueEntry");
}
if (sleepQueueEntry.isInUse()) {
throw new Error(
"Suspended thread cannot have an inuse sleepQueueEntry");
}
break;
case WAITING_ENTER:
case WAITING_NOTIFY:
case WAITING_NOTIFY_TIMEOUT:
if (waitForMonitor == null) {
throw new Error("Waiting thread must have a waitForMonitor");
}
if (!queueEntry.isInUse()) {
throw new Error("Waiting thread must have an inuse queueEntry");
}
break;
case YIELDING:
if (!queueEntry.isInUse()) {
throw new Error("Yielding thread must have an inuse queueEntry");
}
if (sleepQueueEntry.isInUse()) {
throw new Error(
"Yielding thread cannot have an inuse sleepQueueEntry");
}
break;
default:
throw new Error("Unknown thread state " + threadState);
}
// Now detect deadlocks
detectDeadlock();
}
/**
* Convert to a String representation.
*
* @return String
* @see java.lang.Object#toString()
*/
public String toString() {
if (javaThread != null) {
return '%' + javaThread.getName() + ", st"
+ STATE_NAMES[threadState] + '%';
} else {
return "%@null@, st" + threadState + '%';
}
}
/**
* Gets the most current stackframe of this thread. This method is only
* valid when this thread is not running.
*
* @return The stack frame
*/
@KernelSpace
@Internal
public abstract Address getStackFrame();
/**
* Gets the most current instruction pointer of this thread. This method is
* only valid when this thread is not running.
*
* @return The instruction pointer
*/
protected abstract Address getInstructionPointer();
/**
* Gets the stackframe of the last system exception of this thread.
*/
protected abstract Address getExceptionStackFrame();
/**
* Gets the instruction pointer of the last system exception of this thread.
*/
protected abstract Address getExceptionInstructionPointer();
/**
* Calculate the end of the stack.
*
* @param stack
* @param stackSize
* @return End address of the stack
*/
protected abstract Address getStackEnd(Object stack, int stackSize);
/**
* Gets a human readable representation of the system exception state.
*
* @return String
*/
public abstract String getReadableErrorState();
/**
* Gets the inherited control context of this thread.
*
* @return Returns the control.
*/
@Internal
public final VmAccessControlContext getContext() {
return this.context;
}
/**
* Sets the inherited control context of this thread.
*
* @param context The control to set.
*/
@Internal
public final void setContext(VmAccessControlContext context) {
this.context = context;
}
/**
* Detect a deadlock on this thread.
*/
@Uninterruptible
private final void detectDeadlock() {
if (isWaiting()) {
walkWaitingThreads(this);
}
}
/**
* Helper for detectDeadlock
*
* @param thread
* @throws UninterruptiblePragma
*/
@Uninterruptible
private final void walkWaitingThreads(VmThread thread) {
if (thread == null) {
return;
}
final Monitor waitForMonitor = thread.waitForMonitor;
if (waitForMonitor == null) {
return;
}
final VmThread owner = waitForMonitor.getOwner();
if (owner == this) {
// We have a deadlock
Unsafe.debug("Deadlock[");
Unsafe.debug(this.asThread().getName());
Unsafe.debug(", ");
Unsafe.debug(owner.asThread().getName());
} else {
walkWaitingThreads(owner);
}
}
/**
* Gets the inSystemException attribute. Reading this attribute also clears
* it.
*
* @return Returns the inSystemException.
*/
final boolean isInSystemException() {
final boolean rc = this.inSystemException;
this.inSystemException = false;
return rc;
}
/**
* Sets the inSystemException state.
*/
final void setInSystemException() {
this.inSystemException = true;
}
/**
* @param isolatedStatics
*/
public final void switchToIsolate(VmIsolatedStatics isolatedStatics) {
final VmProcessor proc = VmMagic.currentProcessor();
this.isolatedStatics = isolatedStatics;
if (proc.currentThread == this) {
proc.setIsolatedStatics(isolatedStatics);
}
}
/**
* {@inheritDoc}
*/
public abstract boolean accept(ObjectVisitor visitor,
VmHeapManager heapManager);
/**
* @return the requiredProcessor
*/
@KernelSpace
@Uninterruptible
protected final VmProcessor getRequiredProcessor() {
return requiredProcessor;
}
/**
* @param requiredProcessor the requiredProcessor to set
*/
protected final void setRequiredProcessor(VmProcessor requiredProcessor) {
this.requiredProcessor = requiredProcessor;
}
/**
* @return the currentProcessor
*/
final VmProcessor getCurrentProcessor() {
return currentProcessor;
}
/**
* @param currentProcessor the currentProcessor to set
*/
final void setCurrentProcessor(VmProcessor currentProcessor) {
this.currentProcessor = currentProcessor;
}
/**
* Gets the stacktrace of a given thread.
*
* @param current
* @return The stacktrace
*/
public static Object[] getStackTrace(VmThread current) {
if (current.inException) {
Unsafe.debug("Exception in getStackTrace");
VmProcessor.current().getArchitecture().getStackReader()
.debugStackTrace();
Unsafe.die("getStackTrace");
return null;
} else {
current.inException = true;
}
if (VmUtils.getVm().getHeapManager().isLowOnMemory()) {
return null;
}
final VmProcessor proc = VmProcessor.current();
final VmStackReader reader = proc.getArchitecture().getStackReader();
final VmStackFrame[] mt;
boolean inSystemException = false;
// Address lastIP = null;
if (current.isInSystemException()) {
proc.disableReschedule(false);
try {
mt = reader.getVmStackTrace(current.getExceptionStackFrame(),
current.getExceptionInstructionPointer(),
STACKTRACE_LIMIT);
} finally {
proc.enableReschedule(false);
}
inSystemException = true;
} else if (current == proc.getCurrentThread()) {
final Address curFrame = VmMagic.getCurrentFrame();
mt = reader.getVmStackTrace(reader.getPrevious(curFrame), reader
.getReturnAddress(curFrame), STACKTRACE_LIMIT);
} else {
proc.disableReschedule(false);
try {
mt = reader.getVmStackTrace(current.getStackFrame(), current
.getInstructionPointer(), STACKTRACE_LIMIT);
// lastIP = current.getInstructionPointer();
} finally {
proc.enableReschedule(false);
}
}
final int cnt = (mt == null) ? 0 : mt.length;
VmType<?> lastClass = null;
// skip the first element which is VMThrowable.fillInStackTrace() ...
// unless this is a system exception
int i = inSystemException ? 0 : 1;
while (i < cnt) {
final VmStackFrame f = mt[i];
if (f == null) {
break;
}
final VmMethod method = f.getMethod();
if (method == null) {
break;
}
final VmType<?> vmClass = method.getDeclaringClass();
if (vmClass == null) {
break;
}
final VmType<?> sClass = vmClass.getSuperClass();
if (lastClass != null && sClass != lastClass && vmClass != lastClass) {
break;
}
final String mname = method.getName();
if (mname == null) {
break;
}
if (!("<init>".equals(mname) || "fillInStackTrace".equals(mname) || "getStackTrace"
.equals(mname))) {
break;
}
lastClass = vmClass;
i++;
}
final VmStackFrame[] st = new VmStackFrame[cnt - i];
int j = 0;
for (; i < cnt; i++) {
st[j++] = mt[i];
}
current.inException = false;
return st;
}
/**
* Create an exception for a system-trapped situation.
*
* @param nr
* @param address
* @return Throwable
* @throws UninterruptiblePragma
*/
@LoadStatics
@PrivilegedActionPragma
public static Throwable systemException(int nr, int address)
throws UninterruptiblePragma {
// if (VmSystem.debug > 0) {
// Unsafe.debugStackTrace();
// }
// Do stack overflows without anything that is not
// absolutely needed
if (nr == EX_STACKOVERFLOW) {
if (true) {
Unsafe.debug("Stack overflow:\n");
Unsafe.debugStackTrace(50);
Unsafe.debug('\n');
}
throw new StackOverflowError();
}
if (false) {
Unsafe.debug(nr);
Unsafe.debug(address);
Unsafe.die("System exception");
}
// Unsafe.debug(nr); Unsafe.debug(address);
final String hexAddress = NumberUtils.hex(address, 8);
final VmThread current = VmProcessor.current().getCurrentThread();
// final String state = " (" + current.getReadableErrorState() + ")";
final String state = "";
// Mark a system exception, so the stacktrace uses the exception frame
// instead of the current frame.
current.setInSystemException();
switch (nr) {
case EX_NULLPOINTER:
return new NullPointerException("NPE at address " + hexAddress
+ state);
case EX_PAGEFAULT:
return new InternalError("Page fault at " + hexAddress + state);
case EX_INDEXOUTOFBOUNDS:
return new ArrayIndexOutOfBoundsException("Out of bounds at index "
+ address + state);
case EX_DIV0:
return new ArithmeticException("Division by zero at address "
+ hexAddress + state);
case EX_ABSTRACTMETHOD:
return new AbstractMethodError("Abstract method at " + hexAddress
+ state);
case EX_STACKOVERFLOW:
return new StackOverflowError();
case EX_COPRO_OR:
throw new ArithmeticException("Coprocessor overrun");
case EX_COPRO_ERR:
throw new ArithmeticException("Coprocessor error");
default:
return new UnknownError("Unknown system-exception at " + hexAddress
+ state);
}
}
public VmIsolatedStatics getIsolatedStatics() {
return isolatedStatics;
}
@Uninterruptible
final Monitor getLastOwnedMonitor() {
return lastOwnedMonitor;
}
@Uninterruptible
final void setLastOwnedMonitor(Monitor lastOwnedMonitor) {
this.lastOwnedMonitor = lastOwnedMonitor;
}
private VmScheduler getScheduler() {
return ((VmImpl) VmUtils.getVm()).getScheduler();
}
}