a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2579 lines
100 KiB
Java
2579 lines
100 KiB
Java
/*
|
|
* Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code 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 General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package java.lang;
|
|
|
|
import java.lang.ref.Reference;
|
|
import java.lang.ref.ReferenceQueue;
|
|
import java.lang.ref.WeakReference;
|
|
import java.security.AccessController;
|
|
import java.security.AccessControlContext;
|
|
import java.security.PrivilegedAction;
|
|
import java.util.Map;
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentMap;
|
|
import java.util.concurrent.locks.LockSupport;
|
|
import sun.nio.ch.Interruptible;
|
|
import sun.reflect.CallerSensitive;
|
|
import sun.reflect.Reflection;
|
|
import sun.security.util.SecurityConstants;
|
|
|
|
|
|
/**
|
|
* A <i>thread</i> is a thread of execution in a program. The Java
|
|
* Virtual Machine allows an application to have multiple threads of
|
|
* execution running concurrently.
|
|
* <p>
|
|
* Every thread has a priority. Threads with higher priority are
|
|
* executed in preference to threads with lower priority. Each thread
|
|
* may or may not also be marked as a daemon. When code running in
|
|
* some thread creates a new <code>Thread</code> object, the new
|
|
* thread has its priority initially set equal to the priority of the
|
|
* creating thread, and is a daemon thread if and only if the
|
|
* creating thread is a daemon.
|
|
* <p>
|
|
* When a Java Virtual Machine starts up, there is usually a single
|
|
* non-daemon thread (which typically calls the method named
|
|
* <code>main</code> of some designated class). The Java Virtual
|
|
* Machine continues to execute threads until either of the following
|
|
* occurs:
|
|
* <ul>
|
|
* <li>The <code>exit</code> method of class <code>Runtime</code> has been
|
|
* called and the security manager has permitted the exit operation
|
|
* to take place.
|
|
* <li>All threads that are not daemon threads have died, either by
|
|
* returning from the call to the <code>run</code> method or by
|
|
* throwing an exception that propagates beyond the <code>run</code>
|
|
* method.
|
|
* </ul>
|
|
* <p>
|
|
* There are two ways to create a new thread of execution. One is to
|
|
* declare a class to be a subclass of <code>Thread</code>. This
|
|
* subclass should override the <code>run</code> method of class
|
|
* <code>Thread</code>. An instance of the subclass can then be
|
|
* allocated and started. For example, a thread that computes primes
|
|
* larger than a stated value could be written as follows:
|
|
* <p><hr><blockquote><pre>
|
|
* class PrimeThread extends Thread {
|
|
* long minPrime;
|
|
* PrimeThread(long minPrime) {
|
|
* this.minPrime = minPrime;
|
|
* }
|
|
*
|
|
* public void run() {
|
|
* // compute primes larger than minPrime
|
|
* . . .
|
|
* }
|
|
* }
|
|
* </pre></blockquote><hr>
|
|
* <p>
|
|
* The following code would then create a thread and start it running:
|
|
* <p><blockquote><pre>
|
|
* PrimeThread p = new PrimeThread(143);
|
|
* p.start();
|
|
* </pre></blockquote>
|
|
* <p>
|
|
* The other way to create a thread is to declare a class that
|
|
* implements the <code>Runnable</code> interface. That class then
|
|
* implements the <code>run</code> method. An instance of the class can
|
|
* then be allocated, passed as an argument when creating
|
|
* <code>Thread</code>, and started. The same example in this other
|
|
* style looks like the following:
|
|
* <p><hr><blockquote><pre>
|
|
* class PrimeRun implements Runnable {
|
|
* long minPrime;
|
|
* PrimeRun(long minPrime) {
|
|
* this.minPrime = minPrime;
|
|
* }
|
|
*
|
|
* public void run() {
|
|
* // compute primes larger than minPrime
|
|
* . . .
|
|
* }
|
|
* }
|
|
* </pre></blockquote><hr>
|
|
* <p>
|
|
* The following code would then create a thread and start it running:
|
|
* <p><blockquote><pre>
|
|
* PrimeRun p = new PrimeRun(143);
|
|
* new Thread(p).start();
|
|
* </pre></blockquote>
|
|
* <p>
|
|
* Every thread has a name for identification purposes. More than
|
|
* one thread may have the same name. If a name is not specified when
|
|
* a thread is created, a new name is generated for it.
|
|
* <p>
|
|
* Unless otherwise noted, passing a {@code null} argument to a constructor
|
|
* or method in this class will cause a {@link NullPointerException} to be
|
|
* thrown.
|
|
*
|
|
* @author unascribed
|
|
* @see Runnable
|
|
* @see Runtime#exit(int)
|
|
* @see #run()
|
|
* @see #stop()
|
|
* @since JDK1.0
|
|
*/
|
|
public
|
|
class Thread implements Runnable {
|
|
// [IKVM]
|
|
static {
|
|
// force the set/getContextClassLoader methods to be JIT compiled, because isCCLOverridden(Thread) depends on it
|
|
// (we don't want to use RuntimeHelpers.PrepareMethod() because it requires full trust)
|
|
Thread dummy = new Thread((Void)null);
|
|
dummy.getContextClassLoader();
|
|
dummy.setContextClassLoader(ClassLoader.DUMMY);
|
|
}
|
|
private Thread(Void _) {
|
|
// body replaced in map.xml
|
|
}
|
|
final class Cleanup {
|
|
private final Thread thread;
|
|
|
|
Cleanup(Thread thread) {
|
|
this.thread = thread;
|
|
}
|
|
|
|
protected void finalize() {
|
|
thread.die();
|
|
}
|
|
}
|
|
/* --- start IKVM specific state --- */
|
|
static final int[] nonDaemonCount = new int[1];
|
|
@cli.System.ThreadStaticAttribute.Annotation
|
|
static Thread current;
|
|
@cli.System.ThreadStaticAttribute.Annotation
|
|
private static Cleanup cleanup;
|
|
private final Object lock = new Object();
|
|
private cli.System.Threading.Thread nativeThread;
|
|
private Throwable stillborn;
|
|
private boolean running; // used only for coordination with stop0(), is never set to false
|
|
private volatile boolean interruptPending;
|
|
private volatile boolean nativeInterruptPending;
|
|
private volatile boolean interruptableWait;
|
|
private boolean timedWait;
|
|
volatile Object parkLock; // used by get/setParkLock in map.xml
|
|
int parkState; // used by cmpxchgParkState in map.xml
|
|
/* --- end IKVM specific state --- */
|
|
|
|
private char name[];
|
|
private int priority;
|
|
private Thread threadQ;
|
|
private long eetop;
|
|
|
|
/* Whether or not to single_step this thread. */
|
|
private boolean single_step;
|
|
|
|
/* Whether or not the thread is a daemon thread. */
|
|
private boolean daemon = false;
|
|
|
|
/* What will be run. */
|
|
private Runnable target;
|
|
|
|
/* The group of this thread */
|
|
private ThreadGroup group;
|
|
|
|
/* The context ClassLoader for this thread */
|
|
private volatile ClassLoader contextClassLoader;
|
|
|
|
/* The inherited AccessControlContext of this thread */
|
|
AccessController.LazyContext lazyInheritedAccessControlContext;
|
|
|
|
/* For autonumbering anonymous threads. */
|
|
private static int threadInitNumber;
|
|
private static synchronized int nextThreadNum() {
|
|
return threadInitNumber++;
|
|
}
|
|
|
|
/* ThreadLocal values pertaining to this thread. This map is maintained
|
|
* by the ThreadLocal class. */
|
|
ThreadLocal.ThreadLocalMap threadLocals = null;
|
|
|
|
/*
|
|
* InheritableThreadLocal values pertaining to this thread. This map is
|
|
* maintained by the InheritableThreadLocal class.
|
|
*/
|
|
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
|
|
|
|
/*
|
|
* The requested stack size for this thread, or 0 if the creator did
|
|
* not specify a stack size. It is up to the VM to do whatever it
|
|
* likes with this number; some VMs will ignore it.
|
|
*/
|
|
private long stackSize;
|
|
|
|
/*
|
|
* JVM-private state that persists after native thread termination.
|
|
*/
|
|
private long nativeParkEventPointer;
|
|
|
|
/*
|
|
* Thread ID
|
|
*/
|
|
private long tid;
|
|
|
|
/* For generating thread ID */
|
|
private static long threadSeqNumber;
|
|
|
|
/* Java thread status for tools,
|
|
* initialized to indicate thread 'not yet started'
|
|
*/
|
|
|
|
private volatile int threadStatus = 0;
|
|
|
|
|
|
private static synchronized long nextThreadID() {
|
|
return ++threadSeqNumber;
|
|
}
|
|
|
|
/**
|
|
* The argument supplied to the current call to
|
|
* java.util.concurrent.locks.LockSupport.park.
|
|
* Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
|
|
* Accessed using java.util.concurrent.locks.LockSupport.getBlocker
|
|
*/
|
|
volatile Object parkBlocker;
|
|
|
|
/* The object in which this thread is blocked in an interruptible I/O
|
|
* operation, if any. The blocker's interrupt method should be invoked
|
|
* after setting this thread's interrupt status.
|
|
*/
|
|
private volatile Interruptible blocker;
|
|
private final Object blockerLock = new Object();
|
|
|
|
/* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
|
|
*/
|
|
void blockedOn(Interruptible b) {
|
|
synchronized (blockerLock) {
|
|
blocker = b;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The minimum priority that a thread can have.
|
|
*/
|
|
public final static int MIN_PRIORITY = 1;
|
|
|
|
/**
|
|
* The default priority that is assigned to a thread.
|
|
*/
|
|
public final static int NORM_PRIORITY = 5;
|
|
|
|
/**
|
|
* The maximum priority that a thread can have.
|
|
*/
|
|
public final static int MAX_PRIORITY = 10;
|
|
|
|
/**
|
|
* Returns a reference to the currently executing thread object.
|
|
*
|
|
* @return the currently executing thread.
|
|
*/
|
|
public static Thread currentThread() {
|
|
Thread c = current;
|
|
if (c == null) {
|
|
c = new Thread(getMainThreadGroup());
|
|
}
|
|
return c;
|
|
}
|
|
|
|
private static native ThreadGroup getMainThreadGroup();
|
|
|
|
/**
|
|
* A hint to the scheduler that the current thread is willing to yield
|
|
* its current use of a processor. The scheduler is free to ignore this
|
|
* hint.
|
|
*
|
|
* <p> Yield is a heuristic attempt to improve relative progression
|
|
* between threads that would otherwise over-utilise a CPU. Its use
|
|
* should be combined with detailed profiling and benchmarking to
|
|
* ensure that it actually has the desired effect.
|
|
*
|
|
* <p> It is rarely appropriate to use this method. It may be useful
|
|
* for debugging or testing purposes, where it may help to reproduce
|
|
* bugs due to race conditions. It may also be useful when designing
|
|
* concurrency control constructs such as the ones in the
|
|
* {@link java.util.concurrent.locks} package.
|
|
*/
|
|
public static void yield() {
|
|
cli.System.Threading.Thread.Sleep(0);
|
|
}
|
|
|
|
private void enterInterruptableWait(boolean timedWait) throws InterruptedException {
|
|
synchronized (lock) {
|
|
if (interruptPending) {
|
|
interruptPending = false;
|
|
throw new InterruptedException();
|
|
}
|
|
interruptableWait = true;
|
|
this.timedWait = timedWait;
|
|
}
|
|
}
|
|
|
|
private void leaveInterruptableWait() throws InterruptedException {
|
|
cli.System.Threading.ThreadInterruptedException dotnetInterrupt = null;
|
|
interruptableWait = false;
|
|
for (; ; ) {
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadInterruptedException();
|
|
synchronized (lock) {
|
|
if (nativeInterruptPending) {
|
|
nativeInterruptPending = false;
|
|
// HACK if there is a pending Interrupt (on the .NET thread), we need to consume that
|
|
// (if there was no contention on "lock (this)" above the interrupted state isn't checked)
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadInterruptedException();
|
|
cli.System.Threading.Thread t = cli.System.Threading.Thread.get_CurrentThread();
|
|
// the obvious thing to do would be t.Interrupt() / t.Join(),
|
|
// but for some reason that causes a regression in JSR166TestCase (probably a CLR bug)
|
|
// so we waste a time slice... sigh.
|
|
t.Join(1);
|
|
}
|
|
catch (cli.System.Threading.ThreadInterruptedException _) {
|
|
}
|
|
}
|
|
if (interruptPending) {
|
|
interruptPending = false;
|
|
throw new InterruptedException();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
catch (cli.System.Threading.ThreadInterruptedException x) {
|
|
dotnetInterrupt = x;
|
|
nativeInterruptPending = false;
|
|
}
|
|
}
|
|
if (dotnetInterrupt != null) {
|
|
ikvm.runtime.Util.throwException(dotnetInterrupt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Causes the currently executing thread to sleep (temporarily cease
|
|
* execution) for the specified number of milliseconds, subject to
|
|
* the precision and accuracy of system timers and schedulers. The thread
|
|
* does not lose ownership of any monitors.
|
|
*
|
|
* @param millis
|
|
* the length of time to sleep in milliseconds
|
|
*
|
|
* @throws IllegalArgumentException
|
|
* if the value of {@code millis} is negative
|
|
*
|
|
* @throws InterruptedException
|
|
* if any thread has interrupted the current thread. The
|
|
* <i>interrupted status</i> of the current thread is
|
|
* cleared when this exception is thrown.
|
|
*/
|
|
public static void sleep(long millis) throws InterruptedException {
|
|
if (millis < 0) {
|
|
throw new IllegalArgumentException("timeout value is negative");
|
|
}
|
|
Thread c = currentThread();
|
|
c.enterInterruptableWait(true);
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadInterruptedException();
|
|
for (long iter = millis / Integer.MAX_VALUE; iter != 0; iter--)
|
|
{
|
|
cli.System.Threading.Thread.Sleep(Integer.MAX_VALUE);
|
|
}
|
|
cli.System.Threading.Thread.Sleep((int)(millis % Integer.MAX_VALUE));
|
|
}
|
|
catch (cli.System.Threading.ThreadInterruptedException _) {
|
|
}
|
|
finally {
|
|
c.leaveInterruptableWait();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Causes the currently executing thread to sleep (temporarily cease
|
|
* execution) for the specified number of milliseconds plus the specified
|
|
* number of nanoseconds, subject to the precision and accuracy of system
|
|
* timers and schedulers. The thread does not lose ownership of any
|
|
* monitors.
|
|
*
|
|
* @param millis
|
|
* the length of time to sleep in milliseconds
|
|
*
|
|
* @param nanos
|
|
* {@code 0-999999} additional nanoseconds to sleep
|
|
*
|
|
* @throws IllegalArgumentException
|
|
* if the value of {@code millis} is negative, or the value of
|
|
* {@code nanos} is not in the range {@code 0-999999}
|
|
*
|
|
* @throws InterruptedException
|
|
* if any thread has interrupted the current thread. The
|
|
* <i>interrupted status</i> of the current thread is
|
|
* cleared when this exception is thrown.
|
|
*/
|
|
public static void sleep(long millis, int nanos)
|
|
throws InterruptedException {
|
|
if (millis < 0) {
|
|
throw new IllegalArgumentException("timeout value is negative");
|
|
}
|
|
|
|
if (nanos < 0 || nanos > 999999) {
|
|
throw new IllegalArgumentException(
|
|
"nanosecond timeout value out of range");
|
|
}
|
|
|
|
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
|
|
millis++;
|
|
}
|
|
|
|
sleep(millis);
|
|
}
|
|
|
|
/**
|
|
* Initializes a Thread.
|
|
*
|
|
* @param g the Thread group
|
|
* @param target the object whose run() method gets called
|
|
* @param name the name of the new Thread
|
|
* @param stackSize the desired stack size for the new thread, or
|
|
* zero to indicate that this parameter is to be ignored.
|
|
*/
|
|
private void init(ThreadGroup g, Runnable target, String name,
|
|
long stackSize) {
|
|
if (name == null) {
|
|
throw new NullPointerException("name cannot be null");
|
|
}
|
|
|
|
Thread parent = currentThread();
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (g == null) {
|
|
/* Determine if it's an applet or not */
|
|
|
|
/* If there is a security manager, ask the security manager
|
|
what to do. */
|
|
if (security != null) {
|
|
g = security.getThreadGroup();
|
|
}
|
|
|
|
/* If the security doesn't have a strong opinion of the matter
|
|
use the parent thread group. */
|
|
if (g == null) {
|
|
g = parent.getThreadGroup();
|
|
}
|
|
}
|
|
|
|
/* checkAccess regardless of whether or not threadgroup is
|
|
explicitly passed in. */
|
|
g.checkAccess();
|
|
|
|
/*
|
|
* Do we have the required permissions?
|
|
*/
|
|
if (security != null) {
|
|
if (isCCLOverridden(this)) {
|
|
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
|
|
}
|
|
}
|
|
|
|
|
|
g.addUnstarted();
|
|
|
|
this.group = g;
|
|
this.daemon = parent.isDaemon();
|
|
this.priority = parent.getPriority();
|
|
this.name = name.toCharArray();
|
|
if (isCCLOverridden(parent))
|
|
this.contextClassLoader = parent.getContextClassLoader();
|
|
else
|
|
this.contextClassLoader = parent.contextClassLoader;
|
|
this.lazyInheritedAccessControlContext = AccessController.getLazyContext(parent.lazyInheritedAccessControlContext);
|
|
this.target = target;
|
|
setPriority(priority);
|
|
if (parent.inheritableThreadLocals != null)
|
|
this.inheritableThreadLocals =
|
|
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
|
|
/* Stash the specified stack size in case the VM cares */
|
|
this.stackSize = stackSize;
|
|
|
|
/* Set thread ID */
|
|
tid = nextThreadID();
|
|
}
|
|
|
|
// [IKVM] constructor for attaching to a .NET thread
|
|
Thread(ThreadGroup g) {
|
|
this.running = true;
|
|
cli.System.Threading.Thread thread = cli.System.Threading.Thread.get_CurrentThread();
|
|
nativeThread = thread;
|
|
String name = thread.get_Name();
|
|
if (name == null) {
|
|
name = "Thread-" + nextThreadNum();
|
|
}
|
|
|
|
this.group = g;
|
|
this.daemon = thread.get_IsBackground();
|
|
this.priority = mapClrPriorityToJava(thread.get_Priority().Value);
|
|
this.name = name.toCharArray();
|
|
this.contextClassLoader = ClassLoader.DUMMY;
|
|
this.threadStatus = 0x0005; /* JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_RUNNABLE */
|
|
|
|
/* Set thread ID */
|
|
tid = nextThreadID();
|
|
|
|
synchronized (g) {
|
|
g.addUnstarted();
|
|
g.add(this);
|
|
}
|
|
|
|
current = this;
|
|
cleanup = new Cleanup(this);
|
|
|
|
if (!daemon) {
|
|
cli.System.Threading.Interlocked.Increment(nonDaemonCount);
|
|
}
|
|
}
|
|
|
|
private static int mapClrPriorityToJava(int priority) {
|
|
// TODO consider supporting -XX:JavaPriorityX_To_OSPriority settings
|
|
switch (priority) {
|
|
case cli.System.Threading.ThreadPriority.Lowest:
|
|
return MIN_PRIORITY;
|
|
case cli.System.Threading.ThreadPriority.BelowNormal:
|
|
return 3;
|
|
default:
|
|
case cli.System.Threading.ThreadPriority.Normal:
|
|
return NORM_PRIORITY;
|
|
case cli.System.Threading.ThreadPriority.AboveNormal:
|
|
return 7;
|
|
case cli.System.Threading.ThreadPriority.Highest:
|
|
return MAX_PRIORITY;
|
|
}
|
|
}
|
|
|
|
private static int mapJavaPriorityToClr(int priority) {
|
|
// TODO consider supporting -XX:JavaPriorityX_To_OSPriority settings
|
|
if (priority == MIN_PRIORITY) {
|
|
return cli.System.Threading.ThreadPriority.Lowest;
|
|
}
|
|
else if (priority > Thread.MIN_PRIORITY && priority < Thread.NORM_PRIORITY) {
|
|
return cli.System.Threading.ThreadPriority.BelowNormal;
|
|
}
|
|
else if (priority == Thread.NORM_PRIORITY) {
|
|
return cli.System.Threading.ThreadPriority.Normal;
|
|
}
|
|
else if (priority > Thread.NORM_PRIORITY && priority < Thread.MAX_PRIORITY) {
|
|
return cli.System.Threading.ThreadPriority.AboveNormal;
|
|
}
|
|
else if (priority == Thread.MAX_PRIORITY) {
|
|
return cli.System.Threading.ThreadPriority.Highest;
|
|
}
|
|
else {
|
|
// can't happen
|
|
return cli.System.Threading.ThreadPriority.Normal;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Throws CloneNotSupportedException as a Thread can not be meaningfully
|
|
* cloned. Construct a new Thread instead.
|
|
*
|
|
* @throws CloneNotSupportedException
|
|
* always
|
|
*/
|
|
@Override
|
|
protected Object clone() throws CloneNotSupportedException {
|
|
throw new CloneNotSupportedException();
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object. This constructor has the same
|
|
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
|
|
* {@code (null, null, gname)}, where {@code gname} is a newly generated
|
|
* name. Automatically generated names are of the form
|
|
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
|
|
*/
|
|
public Thread() {
|
|
init(null, null, "Thread-" + nextThreadNum(), 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object. This constructor has the same
|
|
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
|
|
* {@code (null, target, gname)}, where {@code gname} is a newly generated
|
|
* name. Automatically generated names are of the form
|
|
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
|
|
*
|
|
* @param target
|
|
* the object whose {@code run} method is invoked when this thread
|
|
* is started. If {@code null}, this classes {@code run} method does
|
|
* nothing.
|
|
*/
|
|
public Thread(Runnable target) {
|
|
init(null, target, "Thread-" + nextThreadNum(), 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object. This constructor has the same
|
|
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
|
|
* {@code (group, target, gname)} ,where {@code gname} is a newly generated
|
|
* name. Automatically generated names are of the form
|
|
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
|
|
*
|
|
* @param group
|
|
* the thread group. If {@code null} and there is a security
|
|
* manager, the group is determined by {@linkplain
|
|
* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
|
|
* If there is not a security manager or {@code
|
|
* SecurityManager.getThreadGroup()} returns {@code null}, the group
|
|
* is set to the current thread's thread group.
|
|
*
|
|
* @param target
|
|
* the object whose {@code run} method is invoked when this thread
|
|
* is started. If {@code null}, this thread's run method is invoked.
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot create a thread in the specified
|
|
* thread group
|
|
*/
|
|
public Thread(ThreadGroup group, Runnable target) {
|
|
init(group, target, "Thread-" + nextThreadNum(), 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object. This constructor has the same
|
|
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
|
|
* {@code (null, null, name)}.
|
|
*
|
|
* @param name
|
|
* the name of the new thread
|
|
*/
|
|
public Thread(String name) {
|
|
init(null, null, name, 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object. This constructor has the same
|
|
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
|
|
* {@code (group, null, name)}.
|
|
*
|
|
* @param group
|
|
* the thread group. If {@code null} and there is a security
|
|
* manager, the group is determined by {@linkplain
|
|
* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
|
|
* If there is not a security manager or {@code
|
|
* SecurityManager.getThreadGroup()} returns {@code null}, the group
|
|
* is set to the current thread's thread group.
|
|
*
|
|
* @param name
|
|
* the name of the new thread
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot create a thread in the specified
|
|
* thread group
|
|
*/
|
|
public Thread(ThreadGroup group, String name) {
|
|
init(group, null, name, 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object. This constructor has the same
|
|
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
|
|
* {@code (null, target, name)}.
|
|
*
|
|
* @param target
|
|
* the object whose {@code run} method is invoked when this thread
|
|
* is started. If {@code null}, this thread's run method is invoked.
|
|
*
|
|
* @param name
|
|
* the name of the new thread
|
|
*/
|
|
public Thread(Runnable target, String name) {
|
|
init(null, target, name, 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object so that it has {@code target}
|
|
* as its run object, has the specified {@code name} as its name,
|
|
* and belongs to the thread group referred to by {@code group}.
|
|
*
|
|
* <p>If there is a security manager, its
|
|
* {@link SecurityManager#checkAccess(ThreadGroup) checkAccess}
|
|
* method is invoked with the ThreadGroup as its argument.
|
|
*
|
|
* <p>In addition, its {@code checkPermission} method is invoked with
|
|
* the {@code RuntimePermission("enableContextClassLoaderOverride")}
|
|
* permission when invoked directly or indirectly by the constructor
|
|
* of a subclass which overrides the {@code getContextClassLoader}
|
|
* or {@code setContextClassLoader} methods.
|
|
*
|
|
* <p>The priority of the newly created thread is set equal to the
|
|
* priority of the thread creating it, that is, the currently running
|
|
* thread. The method {@linkplain #setPriority setPriority} may be
|
|
* used to change the priority to a new value.
|
|
*
|
|
* <p>The newly created thread is initially marked as being a daemon
|
|
* thread if and only if the thread creating it is currently marked
|
|
* as a daemon thread. The method {@linkplain #setDaemon setDaemon}
|
|
* may be used to change whether or not a thread is a daemon.
|
|
*
|
|
* @param group
|
|
* the thread group. If {@code null} and there is a security
|
|
* manager, the group is determined by {@linkplain
|
|
* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
|
|
* If there is not a security manager or {@code
|
|
* SecurityManager.getThreadGroup()} returns {@code null}, the group
|
|
* is set to the current thread's thread group.
|
|
*
|
|
* @param target
|
|
* the object whose {@code run} method is invoked when this thread
|
|
* is started. If {@code null}, this thread's run method is invoked.
|
|
*
|
|
* @param name
|
|
* the name of the new thread
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot create a thread in the specified
|
|
* thread group or cannot override the context class loader methods.
|
|
*/
|
|
public Thread(ThreadGroup group, Runnable target, String name) {
|
|
init(group, target, name, 0);
|
|
}
|
|
|
|
/**
|
|
* Allocates a new {@code Thread} object so that it has {@code target}
|
|
* as its run object, has the specified {@code name} as its name,
|
|
* and belongs to the thread group referred to by {@code group}, and has
|
|
* the specified <i>stack size</i>.
|
|
*
|
|
* <p>This constructor is identical to {@link
|
|
* #Thread(ThreadGroup,Runnable,String)} with the exception of the fact
|
|
* that it allows the thread stack size to be specified. The stack size
|
|
* is the approximate number of bytes of address space that the virtual
|
|
* machine is to allocate for this thread's stack. <b>The effect of the
|
|
* {@code stackSize} parameter, if any, is highly platform dependent.</b>
|
|
*
|
|
* <p>On some platforms, specifying a higher value for the
|
|
* {@code stackSize} parameter may allow a thread to achieve greater
|
|
* recursion depth before throwing a {@link StackOverflowError}.
|
|
* Similarly, specifying a lower value may allow a greater number of
|
|
* threads to exist concurrently without throwing an {@link
|
|
* OutOfMemoryError} (or other internal error). The details of
|
|
* the relationship between the value of the <tt>stackSize</tt> parameter
|
|
* and the maximum recursion depth and concurrency level are
|
|
* platform-dependent. <b>On some platforms, the value of the
|
|
* {@code stackSize} parameter may have no effect whatsoever.</b>
|
|
*
|
|
* <p>The virtual machine is free to treat the {@code stackSize}
|
|
* parameter as a suggestion. If the specified value is unreasonably low
|
|
* for the platform, the virtual machine may instead use some
|
|
* platform-specific minimum value; if the specified value is unreasonably
|
|
* high, the virtual machine may instead use some platform-specific
|
|
* maximum. Likewise, the virtual machine is free to round the specified
|
|
* value up or down as it sees fit (or to ignore it completely).
|
|
*
|
|
* <p>Specifying a value of zero for the {@code stackSize} parameter will
|
|
* cause this constructor to behave exactly like the
|
|
* {@code Thread(ThreadGroup, Runnable, String)} constructor.
|
|
*
|
|
* <p><i>Due to the platform-dependent nature of the behavior of this
|
|
* constructor, extreme care should be exercised in its use.
|
|
* The thread stack size necessary to perform a given computation will
|
|
* likely vary from one JRE implementation to another. In light of this
|
|
* variation, careful tuning of the stack size parameter may be required,
|
|
* and the tuning may need to be repeated for each JRE implementation on
|
|
* which an application is to run.</i>
|
|
*
|
|
* <p>Implementation note: Java platform implementers are encouraged to
|
|
* document their implementation's behavior with respect to the
|
|
* {@code stackSize} parameter.
|
|
*
|
|
*
|
|
* @param group
|
|
* the thread group. If {@code null} and there is a security
|
|
* manager, the group is determined by {@linkplain
|
|
* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.
|
|
* If there is not a security manager or {@code
|
|
* SecurityManager.getThreadGroup()} returns {@code null}, the group
|
|
* is set to the current thread's thread group.
|
|
*
|
|
* @param target
|
|
* the object whose {@code run} method is invoked when this thread
|
|
* is started. If {@code null}, this thread's run method is invoked.
|
|
*
|
|
* @param name
|
|
* the name of the new thread
|
|
*
|
|
* @param stackSize
|
|
* the desired stack size for the new thread, or zero to indicate
|
|
* that this parameter is to be ignored.
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot create a thread in the specified
|
|
* thread group
|
|
*
|
|
* @since 1.4
|
|
*/
|
|
public Thread(ThreadGroup group, Runnable target, String name,
|
|
long stackSize) {
|
|
init(group, target, name, stackSize);
|
|
}
|
|
|
|
/**
|
|
* Causes this thread to begin execution; the Java Virtual Machine
|
|
* calls the <code>run</code> method of this thread.
|
|
* <p>
|
|
* The result is that two threads are running concurrently: the
|
|
* current thread (which returns from the call to the
|
|
* <code>start</code> method) and the other thread (which executes its
|
|
* <code>run</code> method).
|
|
* <p>
|
|
* It is never legal to start a thread more than once.
|
|
* In particular, a thread may not be restarted once it has completed
|
|
* execution.
|
|
*
|
|
* @exception IllegalThreadStateException if the thread was already
|
|
* started.
|
|
* @see #run()
|
|
* @see #stop()
|
|
*/
|
|
public synchronized void start() {
|
|
/**
|
|
* This method is not invoked for the main method thread or "system"
|
|
* group threads created/set up by the VM. Any new functionality added
|
|
* to this method in the future may have to also be added to the VM.
|
|
*
|
|
* A zero status value corresponds to state "NEW".
|
|
*/
|
|
if (threadStatus != 0)
|
|
throw new IllegalThreadStateException();
|
|
|
|
/* Notify the group that this thread is about to be started
|
|
* so that it can be added to the group's list of threads
|
|
* and the group's unstarted count can be decremented. */
|
|
group.add(this);
|
|
|
|
boolean started = false;
|
|
try {
|
|
start0();
|
|
started = true;
|
|
} finally {
|
|
try {
|
|
if (!started) {
|
|
group.threadStartFailed(this);
|
|
}
|
|
} catch (Throwable ignore) {
|
|
/* do nothing. If start0 threw a Throwable then
|
|
it will be passed up the call stack */
|
|
}
|
|
}
|
|
}
|
|
|
|
private void start0() {
|
|
cli.System.Threading.ThreadStart threadStart = new cli.System.Threading.ThreadStart(new cli.System.Threading.ThreadStart.Method() {
|
|
@cli.IKVM.Attributes.HideFromJavaAttribute.Annotation
|
|
public void Invoke() {
|
|
threadProc();
|
|
}
|
|
});
|
|
if (stackSize <= 0) {
|
|
nativeThread = new cli.System.Threading.Thread(threadStart);
|
|
}
|
|
else {
|
|
int maxStackSize = (int)Math.min(Math.max(128 * 1024, stackSize), Integer.MAX_VALUE);
|
|
nativeThread = new cli.System.Threading.Thread(threadStart, maxStackSize);
|
|
}
|
|
nativeThread.set_Name(getName());
|
|
nativeThread.set_IsBackground(daemon);
|
|
nativeThread.set_Priority(cli.System.Threading.ThreadPriority.wrap(mapJavaPriorityToClr(priority)));
|
|
String apartment = Props.props.getProperty("ikvm.apartmentstate", "").toLowerCase();
|
|
if ("mta".equals(apartment)) {
|
|
nativeThread.SetApartmentState(cli.System.Threading.ApartmentState.wrap(cli.System.Threading.ApartmentState.MTA));
|
|
}
|
|
else if ("sta".equals(apartment)) {
|
|
nativeThread.SetApartmentState(cli.System.Threading.ApartmentState.wrap(cli.System.Threading.ApartmentState.STA));
|
|
}
|
|
threadStatus = 0x0005; // JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_RUNNABLE
|
|
nativeThread.Start();
|
|
if (!daemon) {
|
|
cli.System.Threading.Interlocked.Increment(nonDaemonCount);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If this thread was constructed using a separate
|
|
* <code>Runnable</code> run object, then that
|
|
* <code>Runnable</code> object's <code>run</code> method is called;
|
|
* otherwise, this method does nothing and returns.
|
|
* <p>
|
|
* Subclasses of <code>Thread</code> should override this method.
|
|
*
|
|
* @see #start()
|
|
* @see #stop()
|
|
* @see #Thread(ThreadGroup, Runnable, String)
|
|
*/
|
|
@Override
|
|
public void run() {
|
|
if (target != null) {
|
|
target.run();
|
|
}
|
|
}
|
|
|
|
// [IKVM] for threads started from Java, this method is called on the thread itself,
|
|
// but for .NET threads it will be called by the finalizer of the Cleanup object.
|
|
// NOTE there might be a race condition here (when the thread's Cleanup object
|
|
// is finalized during AppDomain shutdown while the thread is also exiting on its own),
|
|
// but that doesn't matter because Thread.exit() is safe to call multiple times.
|
|
void die() {
|
|
exit();
|
|
synchronized (lock) {
|
|
nativeThread = null;
|
|
threadStatus = 0x0002; // JVMTI_THREAD_STATE_TERMINATED
|
|
}
|
|
wakeupJoinedThreads();
|
|
if (!daemon) {
|
|
// TODO there is a race condition in the non-daemon counting
|
|
// (setDaemon() isn't synchronized so it may clear/set the daemon flag without the count being affected)
|
|
cli.System.Threading.Interlocked.Decrement(nonDaemonCount);
|
|
}
|
|
if (current == this) {
|
|
current = null;
|
|
// check if we have a cleanup object, this happens if we attach and subsequently detach from JNI code
|
|
if (cleanup != null) {
|
|
cli.System.GC.SuppressFinalize(cleanup);
|
|
cleanup = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void wakeupJoinedThreads() {
|
|
// HACK locking this here isn't ideal, because we might be invoked from
|
|
// the Cleanup object's finalizer and some user code might own the lock and hence
|
|
// block the finalizer thread.
|
|
// A second scenario is that another thread is currently blocking inside stop()
|
|
// (the Thread.Abort() call will block while we are running the finally block)
|
|
// and that thread will own the lock on our thread object.
|
|
boolean locked = false;
|
|
try {
|
|
locked = cli.System.Threading.Monitor.TryEnter(this);
|
|
if (locked) {
|
|
notifyAll();
|
|
} else {
|
|
// HACK schedule an asynchronous notification
|
|
cli.System.Threading.ThreadPool.QueueUserWorkItem(
|
|
new cli.System.Threading.WaitCallback(
|
|
new cli.System.Threading.WaitCallback.Method() {
|
|
public void Invoke(Object thread) {
|
|
synchronized (thread) {
|
|
thread.notifyAll();
|
|
}
|
|
}
|
|
}), this);
|
|
}
|
|
}
|
|
finally {
|
|
if (locked)
|
|
cli.System.Threading.Monitor.Exit(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is called by the system to give a Thread
|
|
* a chance to clean up before it actually exits.
|
|
*/
|
|
private void exit() {
|
|
if (group != null) {
|
|
group.threadTerminated(this);
|
|
group = null;
|
|
}
|
|
/* Aggressively null out all reference fields: see bug 4006245 */
|
|
target = null;
|
|
/* Speed the release of some of these resources */
|
|
threadLocals = null;
|
|
inheritableThreadLocals = null;
|
|
lazyInheritedAccessControlContext = null;
|
|
blocker = null;
|
|
uncaughtExceptionHandler = null;
|
|
}
|
|
|
|
/**
|
|
* Forces the thread to stop executing.
|
|
* <p>
|
|
* If there is a security manager installed, its <code>checkAccess</code>
|
|
* method is called with <code>this</code>
|
|
* as its argument. This may result in a
|
|
* <code>SecurityException</code> being raised (in the current thread).
|
|
* <p>
|
|
* If this thread is different from the current thread (that is, the current
|
|
* thread is trying to stop a thread other than itself), the
|
|
* security manager's <code>checkPermission</code> method (with a
|
|
* <code>RuntimePermission("stopThread")</code> argument) is called in
|
|
* addition.
|
|
* Again, this may result in throwing a
|
|
* <code>SecurityException</code> (in the current thread).
|
|
* <p>
|
|
* The thread represented by this thread is forced to stop whatever
|
|
* it is doing abnormally and to throw a newly created
|
|
* <code>ThreadDeath</code> object as an exception.
|
|
* <p>
|
|
* It is permitted to stop a thread that has not yet been started.
|
|
* If the thread is eventually started, it immediately terminates.
|
|
* <p>
|
|
* An application should not normally try to catch
|
|
* <code>ThreadDeath</code> unless it must do some extraordinary
|
|
* cleanup operation (note that the throwing of
|
|
* <code>ThreadDeath</code> causes <code>finally</code> clauses of
|
|
* <code>try</code> statements to be executed before the thread
|
|
* officially dies). If a <code>catch</code> clause catches a
|
|
* <code>ThreadDeath</code> object, it is important to rethrow the
|
|
* object so that the thread actually dies.
|
|
* <p>
|
|
* The top-level error handler that reacts to otherwise uncaught
|
|
* exceptions does not print out a message or otherwise notify the
|
|
* application if the uncaught exception is an instance of
|
|
* <code>ThreadDeath</code>.
|
|
*
|
|
* @exception SecurityException if the current thread cannot
|
|
* modify this thread.
|
|
* @see #interrupt()
|
|
* @see #checkAccess()
|
|
* @see #run()
|
|
* @see #start()
|
|
* @see ThreadDeath
|
|
* @see ThreadGroup#uncaughtException(Thread,Throwable)
|
|
* @see SecurityManager#checkAccess(Thread)
|
|
* @see SecurityManager#checkPermission
|
|
* @deprecated This method is inherently unsafe. Stopping a thread with
|
|
* Thread.stop causes it to unlock all of the monitors that it
|
|
* has locked (as a natural consequence of the unchecked
|
|
* <code>ThreadDeath</code> exception propagating up the stack). If
|
|
* any of the objects previously protected by these monitors were in
|
|
* an inconsistent state, the damaged objects become visible to
|
|
* other threads, potentially resulting in arbitrary behavior. Many
|
|
* uses of <code>stop</code> should be replaced by code that simply
|
|
* modifies some variable to indicate that the target thread should
|
|
* stop running. The target thread should check this variable
|
|
* regularly, and return from its run method in an orderly fashion
|
|
* if the variable indicates that it is to stop running. If the
|
|
* target thread waits for long periods (on a condition variable,
|
|
* for example), the <code>interrupt</code> method should be used to
|
|
* interrupt the wait.
|
|
* For more information, see
|
|
* <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
|
|
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
|
|
*/
|
|
@Deprecated
|
|
public final void stop() {
|
|
stop(new ThreadDeath());
|
|
}
|
|
|
|
/**
|
|
* Forces the thread to stop executing.
|
|
* <p>
|
|
* If there is a security manager installed, the <code>checkAccess</code>
|
|
* method of this thread is called, which may result in a
|
|
* <code>SecurityException</code> being raised (in the current thread).
|
|
* <p>
|
|
* If this thread is different from the current thread (that is, the current
|
|
* thread is trying to stop a thread other than itself) or
|
|
* <code>obj</code> is not an instance of <code>ThreadDeath</code>, the
|
|
* security manager's <code>checkPermission</code> method (with the
|
|
* <code>RuntimePermission("stopThread")</code> argument) is called in
|
|
* addition.
|
|
* Again, this may result in throwing a
|
|
* <code>SecurityException</code> (in the current thread).
|
|
* <p>
|
|
* If the argument <code>obj</code> is null, a
|
|
* <code>NullPointerException</code> is thrown (in the current thread).
|
|
* <p>
|
|
* The thread represented by this thread is forced to stop
|
|
* whatever it is doing abnormally and to throw the
|
|
* <code>Throwable</code> object <code>obj</code> as an exception. This
|
|
* is an unusual action to take; normally, the <code>stop</code> method
|
|
* that takes no arguments should be used.
|
|
* <p>
|
|
* It is permitted to stop a thread that has not yet been started.
|
|
* If the thread is eventually started, it immediately terminates.
|
|
*
|
|
* @param obj the Throwable object to be thrown.
|
|
* @exception SecurityException if the current thread cannot modify
|
|
* this thread.
|
|
* @throws NullPointerException if obj is <tt>null</tt>.
|
|
* @see #interrupt()
|
|
* @see #checkAccess()
|
|
* @see #run()
|
|
* @see #start()
|
|
* @see #stop()
|
|
* @see SecurityManager#checkAccess(Thread)
|
|
* @see SecurityManager#checkPermission
|
|
* @deprecated This method is inherently unsafe. See {@link #stop()}
|
|
* for details. An additional danger of this
|
|
* method is that it may be used to generate exceptions that the
|
|
* target thread is unprepared to handle (including checked
|
|
* exceptions that the thread could not possibly throw, were it
|
|
* not for this method).
|
|
* For more information, see
|
|
* <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
|
|
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
|
|
*/
|
|
@Deprecated
|
|
public final synchronized void stop(Throwable obj) {
|
|
if (obj == null)
|
|
throw new NullPointerException();
|
|
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
checkAccess();
|
|
if ((this != Thread.currentThread()) ||
|
|
(!(obj instanceof ThreadDeath))) {
|
|
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
|
|
}
|
|
}
|
|
// A zero status value corresponds to "NEW", it can't change to
|
|
// not-NEW because we hold the lock.
|
|
if (threadStatus != 0) {
|
|
resume(); // Wake up thread if it was suspended; no-op otherwise
|
|
}
|
|
|
|
// The VM can handle all thread states
|
|
stop0(obj);
|
|
}
|
|
|
|
/**
|
|
* Interrupts this thread.
|
|
*
|
|
* <p> Unless the current thread is interrupting itself, which is
|
|
* always permitted, the {@link #checkAccess() checkAccess} method
|
|
* of this thread is invoked, which may cause a {@link
|
|
* SecurityException} to be thrown.
|
|
*
|
|
* <p> If this thread is blocked in an invocation of the {@link
|
|
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
|
|
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
|
|
* class, or of the {@link #join()}, {@link #join(long)}, {@link
|
|
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
|
|
* methods of this class, then its interrupt status will be cleared and it
|
|
* will receive an {@link InterruptedException}.
|
|
*
|
|
* <p> If this thread is blocked in an I/O operation upon an {@link
|
|
* java.nio.channels.InterruptibleChannel </code>interruptible
|
|
* channel<code>} then the channel will be closed, the thread's interrupt
|
|
* status will be set, and the thread will receive a {@link
|
|
* java.nio.channels.ClosedByInterruptException}.
|
|
*
|
|
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
|
|
* then the thread's interrupt status will be set and it will return
|
|
* immediately from the selection operation, possibly with a non-zero
|
|
* value, just as if the selector's {@link
|
|
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
|
|
*
|
|
* <p> If none of the previous conditions hold then this thread's interrupt
|
|
* status will be set. </p>
|
|
*
|
|
* <p> Interrupting a thread that is not alive need not have any effect.
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot modify this thread
|
|
*
|
|
* @revised 6.0
|
|
* @spec JSR-51
|
|
*/
|
|
public void interrupt() {
|
|
if (this != Thread.currentThread())
|
|
checkAccess();
|
|
|
|
synchronized (blockerLock) {
|
|
Interruptible b = blocker;
|
|
if (b != null) {
|
|
interrupt0(); // Just to set the interrupt flag
|
|
b.interrupt(this);
|
|
return;
|
|
}
|
|
}
|
|
interrupt0();
|
|
}
|
|
|
|
/**
|
|
* Tests whether the current thread has been interrupted. The
|
|
* <i>interrupted status</i> of the thread is cleared by this method. In
|
|
* other words, if this method were to be called twice in succession, the
|
|
* second call would return false (unless the current thread were
|
|
* interrupted again, after the first call had cleared its interrupted
|
|
* status and before the second call had examined it).
|
|
*
|
|
* <p>A thread interruption ignored because a thread was not alive
|
|
* at the time of the interrupt will be reflected by this method
|
|
* returning false.
|
|
*
|
|
* @return <code>true</code> if the current thread has been interrupted;
|
|
* <code>false</code> otherwise.
|
|
* @see #isInterrupted()
|
|
* @revised 6.0
|
|
*/
|
|
public static boolean interrupted() {
|
|
Thread current = currentThread();
|
|
if (!current.interruptPending) {
|
|
return false;
|
|
}
|
|
current.interruptPending = false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Tests whether this thread has been interrupted. The <i>interrupted
|
|
* status</i> of the thread is unaffected by this method.
|
|
*
|
|
* <p>A thread interruption ignored because a thread was not alive
|
|
* at the time of the interrupt will be reflected by this method
|
|
* returning false.
|
|
*
|
|
* @return <code>true</code> if this thread has been interrupted;
|
|
* <code>false</code> otherwise.
|
|
* @see #interrupted()
|
|
* @revised 6.0
|
|
*/
|
|
public boolean isInterrupted() {
|
|
return interruptPending;
|
|
}
|
|
|
|
/**
|
|
* Throws {@link NoSuchMethodError}.
|
|
*
|
|
* @deprecated This method was originally designed to destroy this
|
|
* thread without any cleanup. Any monitors it held would have
|
|
* remained locked. However, the method was never implemented.
|
|
* If if were to be implemented, it would be deadlock-prone in
|
|
* much the manner of {@link #suspend}. If the target thread held
|
|
* a lock protecting a critical system resource when it was
|
|
* destroyed, no thread could ever access this resource again.
|
|
* If another thread ever attempted to lock this resource, deadlock
|
|
* would result. Such deadlocks typically manifest themselves as
|
|
* "frozen" processes. For more information, see
|
|
* <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">
|
|
* Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
|
|
* @throws NoSuchMethodError always
|
|
*/
|
|
@Deprecated
|
|
public void destroy() {
|
|
throw new NoSuchMethodError();
|
|
}
|
|
|
|
/**
|
|
* Tests if this thread is alive. A thread is alive if it has
|
|
* been started and has not yet died.
|
|
*
|
|
* @return <code>true</code> if this thread is alive;
|
|
* <code>false</code> otherwise.
|
|
*/
|
|
public final boolean isAlive() {
|
|
return (threadStatus & 0x0001) != 0;
|
|
}
|
|
|
|
/**
|
|
* Suspends this thread.
|
|
* <p>
|
|
* First, the <code>checkAccess</code> method of this thread is called
|
|
* with no arguments. This may result in throwing a
|
|
* <code>SecurityException </code>(in the current thread).
|
|
* <p>
|
|
* If the thread is alive, it is suspended and makes no further
|
|
* progress unless and until it is resumed.
|
|
*
|
|
* @exception SecurityException if the current thread cannot modify
|
|
* this thread.
|
|
* @see #checkAccess
|
|
* @deprecated This method has been deprecated, as it is
|
|
* inherently deadlock-prone. If the target thread holds a lock on the
|
|
* monitor protecting a critical system resource when it is suspended, no
|
|
* thread can access this resource until the target thread is resumed. If
|
|
* the thread that would resume the target thread attempts to lock this
|
|
* monitor prior to calling <code>resume</code>, deadlock results. Such
|
|
* deadlocks typically manifest themselves as "frozen" processes.
|
|
* For more information, see
|
|
* <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
|
|
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
|
|
*/
|
|
@Deprecated
|
|
public final void suspend() {
|
|
checkAccess();
|
|
suspend0();
|
|
}
|
|
|
|
/**
|
|
* Resumes a suspended thread.
|
|
* <p>
|
|
* First, the <code>checkAccess</code> method of this thread is called
|
|
* with no arguments. This may result in throwing a
|
|
* <code>SecurityException</code> (in the current thread).
|
|
* <p>
|
|
* If the thread is alive but suspended, it is resumed and is
|
|
* permitted to make progress in its execution.
|
|
*
|
|
* @exception SecurityException if the current thread cannot modify this
|
|
* thread.
|
|
* @see #checkAccess
|
|
* @see #suspend()
|
|
* @deprecated This method exists solely for use with {@link #suspend},
|
|
* which has been deprecated because it is deadlock-prone.
|
|
* For more information, see
|
|
* <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
|
|
* are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
|
|
*/
|
|
@Deprecated
|
|
public final void resume() {
|
|
checkAccess();
|
|
resume0();
|
|
}
|
|
|
|
/**
|
|
* Changes the priority of this thread.
|
|
* <p>
|
|
* First the <code>checkAccess</code> method of this thread is called
|
|
* with no arguments. This may result in throwing a
|
|
* <code>SecurityException</code>.
|
|
* <p>
|
|
* Otherwise, the priority of this thread is set to the smaller of
|
|
* the specified <code>newPriority</code> and the maximum permitted
|
|
* priority of the thread's thread group.
|
|
*
|
|
* @param newPriority priority to set this thread to
|
|
* @exception IllegalArgumentException If the priority is not in the
|
|
* range <code>MIN_PRIORITY</code> to
|
|
* <code>MAX_PRIORITY</code>.
|
|
* @exception SecurityException if the current thread cannot modify
|
|
* this thread.
|
|
* @see #getPriority
|
|
* @see #checkAccess()
|
|
* @see #getThreadGroup()
|
|
* @see #MAX_PRIORITY
|
|
* @see #MIN_PRIORITY
|
|
* @see ThreadGroup#getMaxPriority()
|
|
*/
|
|
public final void setPriority(int newPriority) {
|
|
ThreadGroup g;
|
|
checkAccess();
|
|
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
if((g = getThreadGroup()) != null) {
|
|
if (newPriority > g.getMaxPriority()) {
|
|
newPriority = g.getMaxPriority();
|
|
}
|
|
setPriority0(priority = newPriority);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns this thread's priority.
|
|
*
|
|
* @return this thread's priority.
|
|
* @see #setPriority
|
|
*/
|
|
public final int getPriority() {
|
|
return priority;
|
|
}
|
|
|
|
/**
|
|
* Changes the name of this thread to be equal to the argument
|
|
* <code>name</code>.
|
|
* <p>
|
|
* First the <code>checkAccess</code> method of this thread is called
|
|
* with no arguments. This may result in throwing a
|
|
* <code>SecurityException</code>.
|
|
*
|
|
* @param name the new name for this thread.
|
|
* @exception SecurityException if the current thread cannot modify this
|
|
* thread.
|
|
* @see #getName
|
|
* @see #checkAccess()
|
|
*/
|
|
public final void setName(String name) {
|
|
checkAccess();
|
|
this.name = name.toCharArray();
|
|
}
|
|
|
|
/**
|
|
* Returns this thread's name.
|
|
*
|
|
* @return this thread's name.
|
|
* @see #setName(String)
|
|
*/
|
|
public final String getName() {
|
|
return String.valueOf(name);
|
|
}
|
|
|
|
/**
|
|
* Returns the thread group to which this thread belongs.
|
|
* This method returns null if this thread has died
|
|
* (been stopped).
|
|
*
|
|
* @return this thread's thread group.
|
|
*/
|
|
public final ThreadGroup getThreadGroup() {
|
|
return group;
|
|
}
|
|
|
|
/**
|
|
* Returns an estimate of the number of active threads in the current
|
|
* thread's {@linkplain java.lang.ThreadGroup thread group} and its
|
|
* subgroups. Recursively iterates over all subgroups in the current
|
|
* thread's thread group.
|
|
*
|
|
* <p> The value returned is only an estimate because the number of
|
|
* threads may change dynamically while this method traverses internal
|
|
* data structures, and might be affected by the presence of certain
|
|
* system threads. This method is intended primarily for debugging
|
|
* and monitoring purposes.
|
|
*
|
|
* @return an estimate of the number of active threads in the current
|
|
* thread's thread group and in any other thread group that
|
|
* has the current thread's thread group as an ancestor
|
|
*/
|
|
public static int activeCount() {
|
|
return currentThread().getThreadGroup().activeCount();
|
|
}
|
|
|
|
/**
|
|
* Copies into the specified array every active thread in the current
|
|
* thread's thread group and its subgroups. This method simply
|
|
* invokes the {@link java.lang.ThreadGroup#enumerate(Thread[])}
|
|
* method of the current thread's thread group.
|
|
*
|
|
* <p> An application might use the {@linkplain #activeCount activeCount}
|
|
* method to get an estimate of how big the array should be, however
|
|
* <i>if the array is too short to hold all the threads, the extra threads
|
|
* are silently ignored.</i> If it is critical to obtain every active
|
|
* thread in the current thread's thread group and its subgroups, the
|
|
* invoker should verify that the returned int value is strictly less
|
|
* than the length of {@code tarray}.
|
|
*
|
|
* <p> Due to the inherent race condition in this method, it is recommended
|
|
* that the method only be used for debugging and monitoring purposes.
|
|
*
|
|
* @param tarray
|
|
* an array into which to put the list of threads
|
|
*
|
|
* @return the number of threads put into the array
|
|
*
|
|
* @throws SecurityException
|
|
* if {@link java.lang.ThreadGroup#checkAccess} determines that
|
|
* the current thread cannot access its thread group
|
|
*/
|
|
public static int enumerate(Thread tarray[]) {
|
|
return currentThread().getThreadGroup().enumerate(tarray);
|
|
}
|
|
|
|
/**
|
|
* Counts the number of stack frames in this thread. The thread must
|
|
* be suspended.
|
|
*
|
|
* @return the number of stack frames in this thread.
|
|
* @exception IllegalThreadStateException if this thread is not
|
|
* suspended.
|
|
* @deprecated The definition of this call depends on {@link #suspend},
|
|
* which is deprecated. Further, the results of this call
|
|
* were never well-defined.
|
|
*/
|
|
@Deprecated
|
|
public int countStackFrames() {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Waits at most {@code millis} milliseconds for this thread to
|
|
* die. A timeout of {@code 0} means to wait forever.
|
|
*
|
|
* <p> This implementation uses a loop of {@code this.wait} calls
|
|
* conditioned on {@code this.isAlive}. As a thread terminates the
|
|
* {@code this.notifyAll} method is invoked. It is recommended that
|
|
* applications not use {@code wait}, {@code notify}, or
|
|
* {@code notifyAll} on {@code Thread} instances.
|
|
*
|
|
* @param millis
|
|
* the time to wait in milliseconds
|
|
*
|
|
* @throws IllegalArgumentException
|
|
* if the value of {@code millis} is negative
|
|
*
|
|
* @throws InterruptedException
|
|
* if any thread has interrupted the current thread. The
|
|
* <i>interrupted status</i> of the current thread is
|
|
* cleared when this exception is thrown.
|
|
*/
|
|
public final synchronized void join(long millis)
|
|
throws InterruptedException {
|
|
long base = System.currentTimeMillis();
|
|
long now = 0;
|
|
|
|
if (millis < 0) {
|
|
throw new IllegalArgumentException("timeout value is negative");
|
|
}
|
|
|
|
if (millis == 0) {
|
|
while (isAlive()) {
|
|
wait(0);
|
|
}
|
|
} else {
|
|
while (isAlive()) {
|
|
long delay = millis - now;
|
|
if (delay <= 0) {
|
|
break;
|
|
}
|
|
wait(delay);
|
|
now = System.currentTimeMillis() - base;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waits at most {@code millis} milliseconds plus
|
|
* {@code nanos} nanoseconds for this thread to die.
|
|
*
|
|
* <p> This implementation uses a loop of {@code this.wait} calls
|
|
* conditioned on {@code this.isAlive}. As a thread terminates the
|
|
* {@code this.notifyAll} method is invoked. It is recommended that
|
|
* applications not use {@code wait}, {@code notify}, or
|
|
* {@code notifyAll} on {@code Thread} instances.
|
|
*
|
|
* @param millis
|
|
* the time to wait in milliseconds
|
|
*
|
|
* @param nanos
|
|
* {@code 0-999999} additional nanoseconds to wait
|
|
*
|
|
* @throws IllegalArgumentException
|
|
* if the value of {@code millis} is negative, or the value
|
|
* of {@code nanos} is not in the range {@code 0-999999}
|
|
*
|
|
* @throws InterruptedException
|
|
* if any thread has interrupted the current thread. The
|
|
* <i>interrupted status</i> of the current thread is
|
|
* cleared when this exception is thrown.
|
|
*/
|
|
public final synchronized void join(long millis, int nanos)
|
|
throws InterruptedException {
|
|
|
|
if (millis < 0) {
|
|
throw new IllegalArgumentException("timeout value is negative");
|
|
}
|
|
|
|
if (nanos < 0 || nanos > 999999) {
|
|
throw new IllegalArgumentException(
|
|
"nanosecond timeout value out of range");
|
|
}
|
|
|
|
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
|
|
millis++;
|
|
}
|
|
|
|
join(millis);
|
|
}
|
|
|
|
/**
|
|
* Waits for this thread to die.
|
|
*
|
|
* <p> An invocation of this method behaves in exactly the same
|
|
* way as the invocation
|
|
*
|
|
* <blockquote>
|
|
* {@linkplain #join(long) join}{@code (0)}
|
|
* </blockquote>
|
|
*
|
|
* @throws InterruptedException
|
|
* if any thread has interrupted the current thread. The
|
|
* <i>interrupted status</i> of the current thread is
|
|
* cleared when this exception is thrown.
|
|
*/
|
|
public final void join() throws InterruptedException {
|
|
join(0);
|
|
}
|
|
|
|
/**
|
|
* Prints a stack trace of the current thread to the standard error stream.
|
|
* This method is used only for debugging.
|
|
*
|
|
* @see Throwable#printStackTrace()
|
|
*/
|
|
public static void dumpStack() {
|
|
new Exception("Stack trace").printStackTrace();
|
|
}
|
|
|
|
/**
|
|
* Marks this thread as either a {@linkplain #isDaemon daemon} thread
|
|
* or a user thread. The Java Virtual Machine exits when the only
|
|
* threads running are all daemon threads.
|
|
*
|
|
* <p> This method must be invoked before the thread is started.
|
|
*
|
|
* @param on
|
|
* if {@code true}, marks this thread as a daemon thread
|
|
*
|
|
* @throws IllegalThreadStateException
|
|
* if this thread is {@linkplain #isAlive alive}
|
|
*
|
|
* @throws SecurityException
|
|
* if {@link #checkAccess} determines that the current
|
|
* thread cannot modify this thread
|
|
*/
|
|
public final void setDaemon(boolean on) {
|
|
checkAccess();
|
|
if (isAlive()) {
|
|
throw new IllegalThreadStateException();
|
|
}
|
|
daemon = on;
|
|
}
|
|
|
|
/**
|
|
* Tests if this thread is a daemon thread.
|
|
*
|
|
* @return <code>true</code> if this thread is a daemon thread;
|
|
* <code>false</code> otherwise.
|
|
* @see #setDaemon(boolean)
|
|
*/
|
|
public final boolean isDaemon() {
|
|
return daemon;
|
|
}
|
|
|
|
/**
|
|
* Determines if the currently running thread has permission to
|
|
* modify this thread.
|
|
* <p>
|
|
* If there is a security manager, its <code>checkAccess</code> method
|
|
* is called with this thread as its argument. This may result in
|
|
* throwing a <code>SecurityException</code>.
|
|
*
|
|
* @exception SecurityException if the current thread is not allowed to
|
|
* access this thread.
|
|
* @see SecurityManager#checkAccess(Thread)
|
|
*/
|
|
public final void checkAccess() {
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkAccess(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a string representation of this thread, including the
|
|
* thread's name, priority, and thread group.
|
|
*
|
|
* @return a string representation of this thread.
|
|
*/
|
|
public String toString() {
|
|
ThreadGroup group = getThreadGroup();
|
|
if (group != null) {
|
|
return "Thread[" + getName() + "," + getPriority() + "," +
|
|
group.getName() + "]";
|
|
} else {
|
|
return "Thread[" + getName() + "," + getPriority() + "," +
|
|
"" + "]";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the context ClassLoader for this Thread. The context
|
|
* ClassLoader is provided by the creator of the thread for use
|
|
* by code running in this thread when loading classes and resources.
|
|
* If not {@linkplain #setContextClassLoader set}, the default is the
|
|
* ClassLoader context of the parent Thread. The context ClassLoader of the
|
|
* primordial thread is typically set to the class loader used to load the
|
|
* application.
|
|
*
|
|
* <p>If a security manager is present, and the invoker's class loader is not
|
|
* {@code null} and is not the same as or an ancestor of the context class
|
|
* loader, then this method invokes the security manager's {@link
|
|
* SecurityManager#checkPermission(java.security.Permission) checkPermission}
|
|
* method with a {@link RuntimePermission RuntimePermission}{@code
|
|
* ("getClassLoader")} permission to verify that retrieval of the context
|
|
* class loader is permitted.
|
|
*
|
|
* @return the context ClassLoader for this Thread, or {@code null}
|
|
* indicating the system class loader (or, failing that, the
|
|
* bootstrap class loader)
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot get the context ClassLoader
|
|
*
|
|
* @since 1.2
|
|
*/
|
|
@CallerSensitive
|
|
public ClassLoader getContextClassLoader() {
|
|
if (contextClassLoader == ClassLoader.DUMMY) {
|
|
contextClassLoader = ClassLoader.getSystemClassLoader();
|
|
}
|
|
if (contextClassLoader == null)
|
|
return null;
|
|
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
ClassLoader.checkClassLoaderPermission(contextClassLoader,
|
|
Reflection.getCallerClass());
|
|
}
|
|
return contextClassLoader;
|
|
}
|
|
|
|
/**
|
|
* Sets the context ClassLoader for this Thread. The context
|
|
* ClassLoader can be set when a thread is created, and allows
|
|
* the creator of the thread to provide the appropriate class loader,
|
|
* through {@code getContextClassLoader}, to code running in the thread
|
|
* when loading classes and resources.
|
|
*
|
|
* <p>If a security manager is present, its {@link
|
|
* SecurityManager#checkPermission(java.security.Permission) checkPermission}
|
|
* method is invoked with a {@link RuntimePermission RuntimePermission}{@code
|
|
* ("setContextClassLoader")} permission to see if setting the context
|
|
* ClassLoader is permitted.
|
|
*
|
|
* @param cl
|
|
* the context ClassLoader for this Thread, or null indicating the
|
|
* system class loader (or, failing that, the bootstrap class loader)
|
|
*
|
|
* @throws SecurityException
|
|
* if the current thread cannot set the context ClassLoader
|
|
*
|
|
* @since 1.2
|
|
*/
|
|
public void setContextClassLoader(ClassLoader cl) {
|
|
if (cl == ClassLoader.DUMMY) {
|
|
// we're being called by Thread.<clinit> to force this method to be JIT compiled
|
|
return;
|
|
}
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
sm.checkPermission(new RuntimePermission("setContextClassLoader"));
|
|
}
|
|
contextClassLoader = cl;
|
|
}
|
|
|
|
// [IKVM] called by sun.misc.Launcher (via map.xml patch) to initialize the context class loader
|
|
final void initContextClassLoader(ClassLoader cl) {
|
|
// we only set contextClassLoader if it hasn't been set (by user code) previously
|
|
java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
|
.newUpdater(Thread.class, ClassLoader.class, "contextClassLoader")
|
|
.compareAndSet(this, ClassLoader.DUMMY, cl);
|
|
}
|
|
|
|
/**
|
|
* Returns <tt>true</tt> if and only if the current thread holds the
|
|
* monitor lock on the specified object.
|
|
*
|
|
* <p>This method is designed to allow a program to assert that
|
|
* the current thread already holds a specified lock:
|
|
* <pre>
|
|
* assert Thread.holdsLock(obj);
|
|
* </pre>
|
|
*
|
|
* @param obj the object on which to test lock ownership
|
|
* @throws NullPointerException if obj is <tt>null</tt>
|
|
* @return <tt>true</tt> if the current thread holds the monitor lock on
|
|
* the specified object.
|
|
* @since 1.4
|
|
*/
|
|
public static boolean holdsLock(Object obj) {
|
|
if (obj == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
try {
|
|
if (false) throw new cli.System.Threading.SynchronizationLockException();
|
|
// The 1.5 memory model (JSR133) explicitly allows spurious wake-ups from Object.wait,
|
|
// so we abuse Pulse to check if we own the monitor.
|
|
cli.System.Threading.Monitor.Pulse(obj);
|
|
return true;
|
|
}
|
|
catch (cli.System.Threading.SynchronizationLockException _) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static final StackTraceElement[] EMPTY_STACK_TRACE
|
|
= new StackTraceElement[0];
|
|
|
|
/**
|
|
* Returns an array of stack trace elements representing the stack dump
|
|
* of this thread. This method will return a zero-length array if
|
|
* this thread has not started, has started but has not yet been
|
|
* scheduled to run by the system, or has terminated.
|
|
* If the returned array is of non-zero length then the first element of
|
|
* the array represents the top of the stack, which is the most recent
|
|
* method invocation in the sequence. The last element of the array
|
|
* represents the bottom of the stack, which is the least recent method
|
|
* invocation in the sequence.
|
|
*
|
|
* <p>If there is a security manager, and this thread is not
|
|
* the current thread, then the security manager's
|
|
* <tt>checkPermission</tt> method is called with a
|
|
* <tt>RuntimePermission("getStackTrace")</tt> permission
|
|
* to see if it's ok to get the stack trace.
|
|
*
|
|
* <p>Some virtual machines may, under some circumstances, omit one
|
|
* or more stack frames from the stack trace. In the extreme case,
|
|
* a virtual machine that has no stack trace information concerning
|
|
* this thread is permitted to return a zero-length array from this
|
|
* method.
|
|
*
|
|
* @return an array of <tt>StackTraceElement</tt>,
|
|
* each represents one stack frame.
|
|
*
|
|
* @throws SecurityException
|
|
* if a security manager exists and its
|
|
* <tt>checkPermission</tt> method doesn't allow
|
|
* getting the stack trace of thread.
|
|
* @see SecurityManager#checkPermission
|
|
* @see RuntimePermission
|
|
* @see Throwable#getStackTrace
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
public StackTraceElement[] getStackTrace() {
|
|
if (this != Thread.currentThread()) {
|
|
// check for getStackTrace permission
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkPermission(
|
|
SecurityConstants.GET_STACK_TRACE_PERMISSION);
|
|
}
|
|
// optimization so we do not call into the vm for threads that
|
|
// have not yet started or have terminated
|
|
if (!isAlive()) {
|
|
return EMPTY_STACK_TRACE;
|
|
}
|
|
StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this});
|
|
StackTraceElement[] stackTrace = stackTraceArray[0];
|
|
// a thread that was alive during the previous isAlive call may have
|
|
// since terminated, therefore not having a stacktrace.
|
|
if (stackTrace == null) {
|
|
stackTrace = EMPTY_STACK_TRACE;
|
|
}
|
|
return stackTrace;
|
|
} else {
|
|
// Don't need JVM help for current thread
|
|
return (new Exception()).getStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a map of stack traces for all live threads.
|
|
* The map keys are threads and each map value is an array of
|
|
* <tt>StackTraceElement</tt> that represents the stack dump
|
|
* of the corresponding <tt>Thread</tt>.
|
|
* The returned stack traces are in the format specified for
|
|
* the {@link #getStackTrace getStackTrace} method.
|
|
*
|
|
* <p>The threads may be executing while this method is called.
|
|
* The stack trace of each thread only represents a snapshot and
|
|
* each stack trace may be obtained at different time. A zero-length
|
|
* array will be returned in the map value if the virtual machine has
|
|
* no stack trace information about a thread.
|
|
*
|
|
* <p>If there is a security manager, then the security manager's
|
|
* <tt>checkPermission</tt> method is called with a
|
|
* <tt>RuntimePermission("getStackTrace")</tt> permission as well as
|
|
* <tt>RuntimePermission("modifyThreadGroup")</tt> permission
|
|
* to see if it is ok to get the stack trace of all threads.
|
|
*
|
|
* @return a <tt>Map</tt> from <tt>Thread</tt> to an array of
|
|
* <tt>StackTraceElement</tt> that represents the stack trace of
|
|
* the corresponding thread.
|
|
*
|
|
* @throws SecurityException
|
|
* if a security manager exists and its
|
|
* <tt>checkPermission</tt> method doesn't allow
|
|
* getting the stack trace of thread.
|
|
* @see #getStackTrace
|
|
* @see SecurityManager#checkPermission
|
|
* @see RuntimePermission
|
|
* @see Throwable#getStackTrace
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
|
|
// check for getStackTrace permission
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkPermission(
|
|
SecurityConstants.GET_STACK_TRACE_PERMISSION);
|
|
security.checkPermission(
|
|
SecurityConstants.MODIFY_THREADGROUP_PERMISSION);
|
|
}
|
|
|
|
// Get a snapshot of the list of all threads
|
|
Thread[] threads = getThreads();
|
|
StackTraceElement[][] traces = dumpThreads(threads);
|
|
Map<Thread, StackTraceElement[]> m = new HashMap<>(threads.length);
|
|
for (int i = 0; i < threads.length; i++) {
|
|
StackTraceElement[] stackTrace = traces[i];
|
|
if (stackTrace != null) {
|
|
m.put(threads[i], stackTrace);
|
|
}
|
|
// else terminated so we don't put it in the map
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
|
|
new RuntimePermission("enableContextClassLoaderOverride");
|
|
|
|
/** cache of subclass security audit results */
|
|
/* Replace with ConcurrentReferenceHashMap when/if it appears in a future
|
|
* release */
|
|
private static class Caches {
|
|
/** cache of subclass security audit results */
|
|
static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
|
|
new ConcurrentHashMap<>();
|
|
|
|
/** queue for WeakReferences to audited subclasses */
|
|
static final ReferenceQueue<Class<?>> subclassAuditsQueue =
|
|
new ReferenceQueue<>();
|
|
}
|
|
|
|
/**
|
|
* Verifies that this (possibly subclass) instance can be constructed
|
|
* without violating security constraints: the subclass must not override
|
|
* security-sensitive non-final methods, or else the
|
|
* "enableContextClassLoaderOverride" RuntimePermission is checked.
|
|
*/
|
|
@cli.System.Runtime.CompilerServices.MethodImplAttribute.Annotation(value = cli.System.Runtime.CompilerServices.MethodImplOptions.__Enum.NoInlining)
|
|
private static native boolean isCCLOverridden(Thread thread); // [IKVM] implemented in map.xml
|
|
|
|
private static boolean isCCLOverridden(Class cl) {
|
|
if (cl == Thread.class)
|
|
return false;
|
|
|
|
processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
|
|
WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
|
|
Boolean result = Caches.subclassAudits.get(key);
|
|
if (result == null) {
|
|
result = Boolean.valueOf(auditSubclass(cl));
|
|
Caches.subclassAudits.putIfAbsent(key, result);
|
|
}
|
|
|
|
return result.booleanValue();
|
|
}
|
|
|
|
/**
|
|
* Performs reflective checks on given subclass to verify that it doesn't
|
|
* override security-sensitive non-final methods. Returns true if the
|
|
* subclass overrides any of the methods, false otherwise.
|
|
*/
|
|
private static boolean auditSubclass(final Class subcl) {
|
|
Boolean result = AccessController.doPrivileged(
|
|
new PrivilegedAction<Boolean>() {
|
|
public Boolean run() {
|
|
for (Class cl = subcl;
|
|
cl != Thread.class;
|
|
cl = cl.getSuperclass())
|
|
{
|
|
try {
|
|
cl.getDeclaredMethod("getContextClassLoader", new Class[0]);
|
|
return Boolean.TRUE;
|
|
} catch (NoSuchMethodException ex) {
|
|
}
|
|
try {
|
|
Class[] params = {ClassLoader.class};
|
|
cl.getDeclaredMethod("setContextClassLoader", params);
|
|
return Boolean.TRUE;
|
|
} catch (NoSuchMethodException ex) {
|
|
}
|
|
}
|
|
return Boolean.FALSE;
|
|
}
|
|
}
|
|
);
|
|
return result.booleanValue();
|
|
}
|
|
|
|
private static StackTraceElement[][] dumpThreads(Thread[] threads) {
|
|
StackTraceElement[][] stacks = new StackTraceElement[threads.length][];
|
|
for (int i = 0; i < threads.length; i++) {
|
|
cli.System.Threading.Thread nativeThread = threads[i].nativeThread;
|
|
if (nativeThread == null) {
|
|
stacks[i] = new StackTraceElement[0];
|
|
} else {
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadStateException();
|
|
boolean suspended = false;
|
|
if ((nativeThread.get_ThreadState().Value & cli.System.Threading.ThreadState.Suspended) == 0 && nativeThread != cli.System.Threading.Thread.get_CurrentThread()) {
|
|
suspended = true;
|
|
nativeThread.Suspend();
|
|
}
|
|
cli.System.Diagnostics.StackTrace stack;
|
|
try {
|
|
stack = new cli.System.Diagnostics.StackTrace(nativeThread, true);
|
|
}
|
|
finally {
|
|
if (suspended) {
|
|
nativeThread.Resume();
|
|
}
|
|
}
|
|
stacks[i] = getStackTrace(stack);
|
|
}
|
|
catch (cli.System.Threading.ThreadStateException _) {
|
|
stacks[i] = new StackTraceElement[0];
|
|
}
|
|
}
|
|
}
|
|
return stacks;
|
|
}
|
|
|
|
private static native StackTraceElement[] getStackTrace(cli.System.Diagnostics.StackTrace stack);
|
|
|
|
private static native Thread[] getThreads();
|
|
|
|
/**
|
|
* Returns the identifier of this Thread. The thread ID is a positive
|
|
* <tt>long</tt> number generated when this thread was created.
|
|
* The thread ID is unique and remains unchanged during its lifetime.
|
|
* When a thread is terminated, this thread ID may be reused.
|
|
*
|
|
* @return this thread's ID.
|
|
* @since 1.5
|
|
*/
|
|
public long getId() {
|
|
return tid;
|
|
}
|
|
|
|
/**
|
|
* A thread state. A thread can be in one of the following states:
|
|
* <ul>
|
|
* <li>{@link #NEW}<br>
|
|
* A thread that has not yet started is in this state.
|
|
* </li>
|
|
* <li>{@link #RUNNABLE}<br>
|
|
* A thread executing in the Java virtual machine is in this state.
|
|
* </li>
|
|
* <li>{@link #BLOCKED}<br>
|
|
* A thread that is blocked waiting for a monitor lock
|
|
* is in this state.
|
|
* </li>
|
|
* <li>{@link #WAITING}<br>
|
|
* A thread that is waiting indefinitely for another thread to
|
|
* perform a particular action is in this state.
|
|
* </li>
|
|
* <li>{@link #TIMED_WAITING}<br>
|
|
* A thread that is waiting for another thread to perform an action
|
|
* for up to a specified waiting time is in this state.
|
|
* </li>
|
|
* <li>{@link #TERMINATED}<br>
|
|
* A thread that has exited is in this state.
|
|
* </li>
|
|
* </ul>
|
|
*
|
|
* <p>
|
|
* A thread can be in only one state at a given point in time.
|
|
* These states are virtual machine states which do not reflect
|
|
* any operating system thread states.
|
|
*
|
|
* @since 1.5
|
|
* @see #getState
|
|
*/
|
|
public enum State {
|
|
/**
|
|
* Thread state for a thread which has not yet started.
|
|
*/
|
|
NEW,
|
|
|
|
/**
|
|
* Thread state for a runnable thread. A thread in the runnable
|
|
* state is executing in the Java virtual machine but it may
|
|
* be waiting for other resources from the operating system
|
|
* such as processor.
|
|
*/
|
|
RUNNABLE,
|
|
|
|
/**
|
|
* Thread state for a thread blocked waiting for a monitor lock.
|
|
* A thread in the blocked state is waiting for a monitor lock
|
|
* to enter a synchronized block/method or
|
|
* reenter a synchronized block/method after calling
|
|
* {@link Object#wait() Object.wait}.
|
|
*/
|
|
BLOCKED,
|
|
|
|
/**
|
|
* Thread state for a waiting thread.
|
|
* A thread is in the waiting state due to calling one of the
|
|
* following methods:
|
|
* <ul>
|
|
* <li>{@link Object#wait() Object.wait} with no timeout</li>
|
|
* <li>{@link #join() Thread.join} with no timeout</li>
|
|
* <li>{@link LockSupport#park() LockSupport.park}</li>
|
|
* </ul>
|
|
*
|
|
* <p>A thread in the waiting state is waiting for another thread to
|
|
* perform a particular action.
|
|
*
|
|
* For example, a thread that has called <tt>Object.wait()</tt>
|
|
* on an object is waiting for another thread to call
|
|
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
|
|
* that object. A thread that has called <tt>Thread.join()</tt>
|
|
* is waiting for a specified thread to terminate.
|
|
*/
|
|
WAITING,
|
|
|
|
/**
|
|
* Thread state for a waiting thread with a specified waiting time.
|
|
* A thread is in the timed waiting state due to calling one of
|
|
* the following methods with a specified positive waiting time:
|
|
* <ul>
|
|
* <li>{@link #sleep Thread.sleep}</li>
|
|
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
|
|
* <li>{@link #join(long) Thread.join} with timeout</li>
|
|
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
|
|
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
|
|
* </ul>
|
|
*/
|
|
TIMED_WAITING,
|
|
|
|
/**
|
|
* Thread state for a terminated thread.
|
|
* The thread has completed execution.
|
|
*/
|
|
TERMINATED;
|
|
}
|
|
|
|
/**
|
|
* Returns the state of this thread.
|
|
* This method is designed for use in monitoring of the system state,
|
|
* not for synchronization control.
|
|
*
|
|
* @return this thread's state.
|
|
* @since 1.5
|
|
*/
|
|
public State getState() {
|
|
// get current thread state
|
|
switch (threadStatus) {
|
|
case 0:
|
|
return State.NEW;
|
|
case 0x0002:
|
|
return State.TERMINATED;
|
|
}
|
|
synchronized (lock) {
|
|
if (interruptableWait) {
|
|
// NOTE if objectWait has satisfied the wait condition (or has been interrupted or has timed-out),
|
|
// it can be blocking on the re-acquire of the monitor, but we have no way of detecting that.
|
|
return timedWait ? State.TIMED_WAITING : State.WAITING;
|
|
}
|
|
}
|
|
cli.System.Threading.Thread nativeThread = this.nativeThread;
|
|
if (nativeThread == null) {
|
|
return State.TERMINATED;
|
|
}
|
|
if ((nativeThread.get_ThreadState().Value & cli.System.Threading.ThreadState.WaitSleepJoin) != 0) {
|
|
return State.BLOCKED;
|
|
}
|
|
return State.RUNNABLE;
|
|
}
|
|
|
|
// Added in JSR-166
|
|
|
|
/**
|
|
* Interface for handlers invoked when a <tt>Thread</tt> abruptly
|
|
* terminates due to an uncaught exception.
|
|
* <p>When a thread is about to terminate due to an uncaught exception
|
|
* the Java Virtual Machine will query the thread for its
|
|
* <tt>UncaughtExceptionHandler</tt> using
|
|
* {@link #getUncaughtExceptionHandler} and will invoke the handler's
|
|
* <tt>uncaughtException</tt> method, passing the thread and the
|
|
* exception as arguments.
|
|
* If a thread has not had its <tt>UncaughtExceptionHandler</tt>
|
|
* explicitly set, then its <tt>ThreadGroup</tt> object acts as its
|
|
* <tt>UncaughtExceptionHandler</tt>. If the <tt>ThreadGroup</tt> object
|
|
* has no
|
|
* special requirements for dealing with the exception, it can forward
|
|
* the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler
|
|
* default uncaught exception handler}.
|
|
*
|
|
* @see #setDefaultUncaughtExceptionHandler
|
|
* @see #setUncaughtExceptionHandler
|
|
* @see ThreadGroup#uncaughtException
|
|
* @since 1.5
|
|
*/
|
|
public interface UncaughtExceptionHandler {
|
|
/**
|
|
* Method invoked when the given thread terminates due to the
|
|
* given uncaught exception.
|
|
* <p>Any exception thrown by this method will be ignored by the
|
|
* Java Virtual Machine.
|
|
* @param t the thread
|
|
* @param e the exception
|
|
*/
|
|
void uncaughtException(Thread t, Throwable e);
|
|
}
|
|
|
|
// null unless explicitly set
|
|
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
|
|
|
|
// null unless explicitly set
|
|
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
|
|
|
|
/**
|
|
* Set the default handler invoked when a thread abruptly terminates
|
|
* due to an uncaught exception, and no other handler has been defined
|
|
* for that thread.
|
|
*
|
|
* <p>Uncaught exception handling is controlled first by the thread, then
|
|
* by the thread's {@link ThreadGroup} object and finally by the default
|
|
* uncaught exception handler. If the thread does not have an explicit
|
|
* uncaught exception handler set, and the thread's thread group
|
|
* (including parent thread groups) does not specialize its
|
|
* <tt>uncaughtException</tt> method, then the default handler's
|
|
* <tt>uncaughtException</tt> method will be invoked.
|
|
* <p>By setting the default uncaught exception handler, an application
|
|
* can change the way in which uncaught exceptions are handled (such as
|
|
* logging to a specific device, or file) for those threads that would
|
|
* already accept whatever "default" behavior the system
|
|
* provided.
|
|
*
|
|
* <p>Note that the default uncaught exception handler should not usually
|
|
* defer to the thread's <tt>ThreadGroup</tt> object, as that could cause
|
|
* infinite recursion.
|
|
*
|
|
* @param eh the object to use as the default uncaught exception handler.
|
|
* If <tt>null</tt> then there is no default handler.
|
|
*
|
|
* @throws SecurityException if a security manager is present and it
|
|
* denies <tt>{@link RuntimePermission}
|
|
* ("setDefaultUncaughtExceptionHandler")</tt>
|
|
*
|
|
* @see #setUncaughtExceptionHandler
|
|
* @see #getUncaughtExceptionHandler
|
|
* @see ThreadGroup#uncaughtException
|
|
* @since 1.5
|
|
*/
|
|
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
sm.checkPermission(
|
|
new RuntimePermission("setDefaultUncaughtExceptionHandler")
|
|
);
|
|
}
|
|
|
|
defaultUncaughtExceptionHandler = eh;
|
|
}
|
|
|
|
/**
|
|
* Returns the default handler invoked when a thread abruptly terminates
|
|
* due to an uncaught exception. If the returned value is <tt>null</tt>,
|
|
* there is no default.
|
|
* @since 1.5
|
|
* @see #setDefaultUncaughtExceptionHandler
|
|
*/
|
|
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
|
|
return defaultUncaughtExceptionHandler;
|
|
}
|
|
|
|
/**
|
|
* Returns the handler invoked when this thread abruptly terminates
|
|
* due to an uncaught exception. If this thread has not had an
|
|
* uncaught exception handler explicitly set then this thread's
|
|
* <tt>ThreadGroup</tt> object is returned, unless this thread
|
|
* has terminated, in which case <tt>null</tt> is returned.
|
|
* @since 1.5
|
|
*/
|
|
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
|
|
return uncaughtExceptionHandler != null ?
|
|
uncaughtExceptionHandler : group;
|
|
}
|
|
|
|
/**
|
|
* Set the handler invoked when this thread abruptly terminates
|
|
* due to an uncaught exception.
|
|
* <p>A thread can take full control of how it responds to uncaught
|
|
* exceptions by having its uncaught exception handler explicitly set.
|
|
* If no such handler is set then the thread's <tt>ThreadGroup</tt>
|
|
* object acts as its handler.
|
|
* @param eh the object to use as this thread's uncaught exception
|
|
* handler. If <tt>null</tt> then this thread has no explicit handler.
|
|
* @throws SecurityException if the current thread is not allowed to
|
|
* modify this thread.
|
|
* @see #setDefaultUncaughtExceptionHandler
|
|
* @see ThreadGroup#uncaughtException
|
|
* @since 1.5
|
|
*/
|
|
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
|
|
checkAccess();
|
|
uncaughtExceptionHandler = eh;
|
|
}
|
|
|
|
/**
|
|
* Dispatch an uncaught exception to the handler. This method is
|
|
* intended to be called only by the JVM.
|
|
*/
|
|
private void dispatchUncaughtException(Throwable e) {
|
|
getUncaughtExceptionHandler().uncaughtException(this, e);
|
|
}
|
|
|
|
/**
|
|
* Removes from the specified map any keys that have been enqueued
|
|
* on the specified reference queue.
|
|
*/
|
|
static void processQueue(ReferenceQueue<Class<?>> queue,
|
|
ConcurrentMap<? extends
|
|
WeakReference<Class<?>>, ?> map)
|
|
{
|
|
Reference<? extends Class<?>> ref;
|
|
while((ref = queue.poll()) != null) {
|
|
map.remove(ref);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Weak key for Class objects.
|
|
**/
|
|
static class WeakClassKey extends WeakReference<Class<?>> {
|
|
/**
|
|
* saved value of the referent's identity hash code, to maintain
|
|
* a consistent hash code after the referent has been cleared
|
|
*/
|
|
private final int hash;
|
|
|
|
/**
|
|
* Create a new WeakClassKey to the given object, registered
|
|
* with a queue.
|
|
*/
|
|
WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
|
|
super(cl, refQueue);
|
|
hash = System.identityHashCode(cl);
|
|
}
|
|
|
|
/**
|
|
* Returns the identity hash code of the original referent.
|
|
*/
|
|
@Override
|
|
public int hashCode() {
|
|
return hash;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given object is this identical
|
|
* WeakClassKey instance, or, if this object's referent has not
|
|
* been cleared, if the given object is another WeakClassKey
|
|
* instance with the identical non-null referent as this one.
|
|
*/
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (obj == this)
|
|
return true;
|
|
|
|
if (obj instanceof WeakClassKey) {
|
|
Object referent = get();
|
|
return (referent != null) &&
|
|
(referent == ((WeakClassKey) obj).get());
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Some private helper methods */
|
|
private synchronized void setPriority0(int newPriority) {
|
|
cli.System.Threading.Thread nativeThread = this.nativeThread;
|
|
if (nativeThread != null) {
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadStateException();
|
|
nativeThread.set_Priority(cli.System.Threading.ThreadPriority.wrap(mapJavaPriorityToClr(newPriority)));
|
|
}
|
|
catch (cli.System.Threading.ThreadStateException _) {
|
|
}
|
|
}
|
|
}
|
|
|
|
private void stop0(Throwable x) {
|
|
synchronized (lock) {
|
|
if (!running) {
|
|
stillborn = x;
|
|
x = null;
|
|
}
|
|
}
|
|
if (x != null) {
|
|
// NOTE we allow ThreadDeath (and its subclasses) to be thrown on every thread, but any
|
|
// other exception is ignored, except if we're throwing it on the current Thread. This
|
|
// is done to allow exception handlers to be type specific, otherwise every exception
|
|
// handler would have to catch ThreadAbortException and look inside it to see if it
|
|
// contains the real exception that we wish to handle.
|
|
// I hope we can get away with this behavior, because Thread.stop() is deprecated
|
|
// anyway. Note that we do allow arbitrary exceptions to be thrown on the current
|
|
// thread, since this is harmless (because they aren't wrapped) and also because it
|
|
// provides some real value, because it is one of the ways you can throw arbitrary checked
|
|
// exceptions from Java.
|
|
if (this == current) {
|
|
sun.misc.Unsafe.getUnsafe().throwException(x);
|
|
}
|
|
else if (x instanceof ThreadDeath) {
|
|
cli.System.Threading.Thread nativeThread = this.nativeThread;
|
|
if (nativeThread == null) {
|
|
return;
|
|
}
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadStateException();
|
|
nativeThread.Abort(x);
|
|
}
|
|
catch (cli.System.Threading.ThreadStateException _) {
|
|
// .NET 2.0 throws a ThreadStateException if the target thread is currently suspended
|
|
// (but it does record the Abort request)
|
|
}
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadStateException();
|
|
int suspend = cli.System.Threading.ThreadState.Suspended | cli.System.Threading.ThreadState.SuspendRequested;
|
|
while ((nativeThread.get_ThreadState().Value & suspend) != 0) {
|
|
nativeThread.Resume();
|
|
}
|
|
}
|
|
catch (cli.System.Threading.ThreadStateException _) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void suspend0() {
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadStateException();
|
|
cli.System.Threading.Thread nativeThread = this.nativeThread;
|
|
if (nativeThread != null) {
|
|
nativeThread.Suspend();
|
|
}
|
|
}
|
|
catch (cli.System.Threading.ThreadStateException _) {
|
|
}
|
|
}
|
|
|
|
private void resume0() {
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadStateException();
|
|
cli.System.Threading.Thread nativeThread = this.nativeThread;
|
|
if (nativeThread != null) {
|
|
nativeThread.Resume();
|
|
}
|
|
}
|
|
catch (cli.System.Threading.ThreadStateException _) {
|
|
}
|
|
}
|
|
|
|
private void interrupt0() {
|
|
synchronized (lock) {
|
|
// if the thread hasn't been started yet or has been terminated, the interrupt is ignored
|
|
// (like on the reference implementation)
|
|
if (nativeThread == null) {
|
|
return;
|
|
}
|
|
if (!interruptPending) {
|
|
interruptPending = true;
|
|
if (interruptableWait) {
|
|
nativeInterruptPending = true;
|
|
nativeThread.Interrupt();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setRunningAndCheckStillborn() throws Throwable {
|
|
Throwable x;
|
|
synchronized (lock) {
|
|
running = true;
|
|
x = stillborn;
|
|
stillborn = null;
|
|
}
|
|
if (x != null) {
|
|
throw x;
|
|
}
|
|
}
|
|
|
|
// [IKVM] this the entry point of thread started from Java
|
|
@cli.IKVM.Attributes.HideFromJavaAttribute.Annotation
|
|
void threadProc() {
|
|
current = this;
|
|
try {
|
|
// the body of the try block is in another method to allow the (limited) try/finally optimizer
|
|
// to properly recognize the try/finally block, because we want to make sure that die()
|
|
// runs in a finally block to prevent it from being asynchronously aborted.
|
|
threadProc2();
|
|
}
|
|
finally {
|
|
die();
|
|
}
|
|
}
|
|
|
|
@cli.IKVM.Attributes.HideFromJavaAttribute.Annotation
|
|
private void threadProc2() {
|
|
try {
|
|
setRunningAndCheckStillborn();
|
|
run();
|
|
}
|
|
catch (Throwable x) {
|
|
try {
|
|
getUncaughtExceptionHandler().uncaughtException(this, x);
|
|
}
|
|
catch (Throwable _) {
|
|
}
|
|
}
|
|
}
|
|
|
|
// [IKVM] this the implementation of Object.wait(long timeout, int nanos). It is hooked up in map.xml.
|
|
static void objectWait(Object o, long timeout, int nanos) throws InterruptedException {
|
|
if (o == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
if (timeout < 0) {
|
|
throw new IllegalArgumentException("timeout value is negative");
|
|
}
|
|
if (nanos < 0 || nanos > 999999) {
|
|
throw new IllegalArgumentException("nanosecond timeout value out of range");
|
|
}
|
|
if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
|
|
timeout++;
|
|
}
|
|
objectWait(o, timeout);
|
|
}
|
|
|
|
// [IKVM] this the implementation of Object.wait(long timeout). It is hooked up in map.xml.
|
|
static void objectWait(Object o, long timeout) throws InterruptedException {
|
|
if (o == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
if (timeout < 0) {
|
|
throw new IllegalArgumentException("timeout value is negative");
|
|
}
|
|
Thread t = currentThread();
|
|
t.enterInterruptableWait(timeout != 0);
|
|
try {
|
|
if (false) throw new cli.System.Threading.ThreadInterruptedException();
|
|
if (timeout == 0 || timeout > 922337203685476L) {
|
|
cli.System.Threading.Monitor.Wait(o);
|
|
}
|
|
else {
|
|
// We wait a maximum of Integer.MAX_VALUE milliseconds, because that is the maximum that Monitor.Wait will wait.
|
|
// Note that the Object.wait() specification allows for spurious wakeups, so this isn't a problem. Trying to
|
|
// emulate a longer wait with multiple Monitor.Wait() calls is not allowed, because that would mean that
|
|
// we acquire and release the synchronization lock multiple times during the wait.
|
|
cli.System.Threading.Monitor.Wait(o, (int)Math.min(timeout, Integer.MAX_VALUE));
|
|
}
|
|
}
|
|
catch (cli.System.Threading.ThreadInterruptedException _) {
|
|
}
|
|
finally {
|
|
t.leaveInterruptableWait();
|
|
}
|
|
}
|
|
}
|