/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.lang;
import com.google.j2objc.annotations.Weak;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/*-[
#import "java/lang/AssertionError.h"
#import "objc-sync.h"
#import <pthread.h>
]-*/
/*-[
@interface NativeThread : NSObject {
@public
pthread_t t;
}
@end
@implementation NativeThread
@end
]-*/
/**
* Simplified iOS version of java.lang.Thread, based on Apache Harmony source
* (both luni-kernel and vmcore). This class uses pthread for thread creation
* and maintains a pthread handle for using other pthread functionality.
* pthread's thread local mechanism (pthread_setspecific) is used to associate
* this wrapper object with the current thread.
*
* @author Tom Ball, Keith Stanger
*/
public class Thread implements Runnable {
private static final int NANOS_PER_MILLI = 1000000;
/** Android source declares this as the native VMThread class. */
private final Object nativeThread;
private Runnable target;
private final long threadId;
private String name;
private final long stackSize;
private volatile State state = State.NEW;
private int priority = NORM_PRIORITY;
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
private boolean isDaemon;
boolean interrupted;
private ClassLoader contextClassLoader;
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/** The object the thread is waiting on (normally null). */
Object blocker;
@Weak
private ThreadGroup threadGroup;
/** the park state of the thread */
private int parkState = ParkState.UNPARKED;
/** The synchronization object responsible for this thread parking. */
private Object parkBlocker = new Object();
/** Callbacks to run on interruption. */
private final List<Runnable> interruptActions = new ArrayList<Runnable>();
/**
* Counter used to generate thread's ID
*/
private static long threadOrdinalNum = 1;
/**
* A representation of a thread's state. A given thread may only be in one
* state at a time.
*/
public enum State {
/**
* The thread has been created, but has never been started.
*/
NEW,
/**
* The thread may be run.
*/
RUNNABLE,
/**
* The thread is blocked and waiting for a lock.
*/
BLOCKED,
/**
* The thread is waiting.
*/
WAITING,
/**
* The thread is waiting for a specified amount of time.
*/
TIMED_WAITING,
/**
* The thread has been terminated.
*/
TERMINATED
}
/** Park states */
private static class ParkState {
/** park state indicating unparked */
private static final int UNPARKED = 1;
/** park state indicating preemptively unparked */
private static final int PREEMPTIVELY_UNPARKED = 2;
/** park state indicating parked */
private static final int PARKED = 3;
}
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
/**
* Holds the default handler for uncaught exceptions, in case there is one.
*/
private static volatile UncaughtExceptionHandler defaultUncaughtHandler =
new SystemUncaughtExceptionHandler();
/**
* <p>
* The maximum priority value allowed for a thread.
* </p>
*/
public final static int MAX_PRIORITY = 10;
/**
* <p>
* The minimum priority value allowed for a thread.
* </p>
*/
public final static int MIN_PRIORITY = 1;
/**
* <p>
* The normal (default) priority value assigned to threads.
* </p>
*/
public final static int NORM_PRIORITY = 5;
/**
* used to generate a default thread name
*/
private static final String THREAD = "Thread-";
// Milliseconds between polls for testing thread completion.
private static final int POLL_INTERVAL = 20;
static {
initializeThreadClass();
}
private static native Object newNativeThread() /*-[
return [[[NativeThread alloc] init] autorelease];
]-*/;
/**
* Constructs a new Thread with no runnable object and a newly generated
* name. The new Thread will belong to the same ThreadGroup as the Thread
* calling this constructor.
*
* @see java.lang.ThreadGroup
*/
public Thread() {
this(null, null, THREAD, 0, null);
}
/**
* Constructs a new Thread with a runnable object and a newly generated
* name. The new Thread will belong to the same ThreadGroup as the Thread
* calling this constructor.
*
* @param runnable a java.lang.Runnable whose method <code>run</code> will
* be executed by the new Thread
* @see java.lang.ThreadGroup
* @see java.lang.Runnable
*/
public Thread(Runnable runnable) {
this(null, runnable, THREAD, 0, null);
}
/**
* Constructs a new Thread with a runnable object and name provided. The new
* Thread will belong to the same ThreadGroup as the Thread calling this
* constructor.
*
* @param runnable a java.lang.Runnable whose method <code>run</code> will
* be executed by the new Thread
* @param threadName Name for the Thread being created
* @see java.lang.ThreadGroup
* @see java.lang.Runnable
*/
public Thread(Runnable runnable, String threadName) {
this(null, runnable, threadName, 0, null);
}
/**
* Constructs a new Thread with no runnable object and the name provided.
* The new Thread will belong to the same ThreadGroup as the Thread calling
* this constructor.
*
* @param threadName Name for the Thread being created
* @see java.lang.ThreadGroup
* @see java.lang.Runnable
*/
public Thread(String threadName) {
this(null, null, threadName, 0, null);
}
/**
* Constructs a new Thread with a runnable object and a newly generated
* name. The new Thread will belong to the ThreadGroup passed as parameter.
*
* @param group ThreadGroup to which the new Thread will belong
* @param runnable a java.lang.Runnable whose method <code>run</code> will
* be executed by the new Thread
* @throws SecurityException if <code>group.checkAccess()</code> fails
* with a SecurityException
* @throws IllegalThreadStateException if <code>group.destroy()</code> has
* already been done
* @see java.lang.ThreadGroup
* @see java.lang.Runnable
* @see java.lang.SecurityException
* @see java.lang.SecurityManager
*/
public Thread(ThreadGroup group, Runnable runnable) {
this(group, runnable, THREAD, 0, null);
}
/**
* Constructs a new Thread with a runnable object, the given name and
* belonging to the ThreadGroup passed as parameter.
*
* @param group ThreadGroup to which the new Thread will belong
* @param runnable a java.lang.Runnable whose method <code>run</code> will
* be executed by the new Thread
* @param threadName Name for the Thread being created
* @param stack Platform dependent stack size
* @throws SecurityException if <code>group.checkAccess()</code> fails
* with a SecurityException
* @throws IllegalThreadStateException if <code>group.destroy()</code> has
* already been done
* @see java.lang.ThreadGroup
* @see java.lang.Runnable
* @see java.lang.SecurityException
* @see java.lang.SecurityManager
*/
public Thread(ThreadGroup group, Runnable runnable, String threadName, long stack) {
this(group, runnable, threadName, stack, null);
}
/**
* Constructs a new Thread with a runnable object, the given name and
* belonging to the ThreadGroup passed as parameter.
*
* @param group ThreadGroup to which the new Thread will belong
* @param runnable a java.lang.Runnable whose method <code>run</code> will
* be executed by the new Thread
* @param threadName Name for the Thread being created
* @throws SecurityException if <code>group.checkAccess()</code> fails
* with a SecurityException
* @throws IllegalThreadStateException if <code>group.destroy()</code> has
* already been done
* @see java.lang.ThreadGroup
* @see java.lang.Runnable
* @see java.lang.SecurityException
* @see java.lang.SecurityManager
*/
public Thread(ThreadGroup group, Runnable runnable, String threadName) {
this(group, runnable, threadName, 0, null);
}
/**
* Constructs a new Thread with no runnable object, the given name and
* belonging to the ThreadGroup passed as parameter.
*
* @param group ThreadGroup to which the new Thread will belong
* @param threadName Name for the Thread being created
* @throws SecurityException if <code>group.checkAccess()</code> fails
* with a SecurityException
* @throws IllegalThreadStateException if <code>group.destroy()</code> has
* already been done
* @see java.lang.ThreadGroup
* @see java.lang.SecurityException
* @see java.lang.SecurityManager
*/
public Thread(ThreadGroup group, String threadName) {
this(group, null, threadName, 0, null);
}
private Thread(
ThreadGroup group, Runnable runnable, String name, long stack, Object nativeThread) {
this.target = runnable;
this.threadId = getNextThreadId();
if (name.equals(THREAD)) {
name += threadId;
}
this.name = name;
this.stackSize = stack;
if (nativeThread == null) {
// Thread is not yet started.
Thread currentThread = currentThread();
nativeThread = newNativeThread();
this.priority = currentThread.getPriority();
if (group == null) {
group = currentThread.getThreadGroup();
}
} else {
// Thread is already running.
state = State.RUNNABLE;
group.add(this);
}
this.threadGroup = group;
this.nativeThread = nativeThread;
}
/*-[
pthread_key_t java_thread_key;
void javaThreadDestructor(void *javaThread) {
JavaLangThread *thread = (JavaLangThread *)javaThread;
[thread exit];
[thread release];
}
void *start_routine(void *arg) {
JavaLangThread *thread = (JavaLangThread *)arg;
pthread_setspecific(java_thread_key, thread);
@autoreleasepool {
@try {
[thread run];
} @catch (NSException *t) {
[thread rethrowWithNSException:t];
} @catch (id error) {
[thread rethrowWithNSException:[NSException exceptionWithName:@"Unknown error"
reason:[error description]
userInfo:nil]];
}
return NULL;
}
}
]-*/
private static Thread createMainThread(Object nativeThread) {
return new Thread(ThreadGroup.mainThreadGroup, null, "main", 0, nativeThread);
}
/**
* Create a Thread wrapper around the main native thread.
*/
private static native void initializeThreadClass() /*-[
if (pthread_key_create(&java_thread_key, &javaThreadDestructor)) {
@throw create_JavaLangAssertionError_initWithId_(@"Failed to create pthread key.");
}
NativeThread *nt = [[[NativeThread alloc] init] autorelease];
nt->t = pthread_self();
JavaLangThread *mainThread = JavaLangThread_createMainThreadWithId_(nt);
pthread_setspecific(java_thread_key, [mainThread retain]);
]-*/;
private static Thread createCurrentThread(Object nativeThread) {
return new Thread(ThreadGroup.mainThreadGroup, null, THREAD, 0, nativeThread);
}
public static native Thread currentThread() /*-[
JavaLangThread *thread = pthread_getspecific(java_thread_key);
if (thread) {
return thread;
}
NativeThread *nt = [[[NativeThread alloc] init] autorelease];
nt->t = pthread_self();
thread = JavaLangThread_createCurrentThreadWithId_(nt);
pthread_setspecific(java_thread_key, [thread retain]);
return thread;
]-*/;
public synchronized void start() {
if (state != State.NEW) {
throw new IllegalThreadStateException("This thread was already started!");
}
threadGroup.add(this);
start0();
if (priority != NORM_PRIORITY) {
nativeSetPriority(priority);
}
state = State.RUNNABLE;
}
private native void start0() /*-[
NativeThread *nt = (NativeThread *)self->nativeThread_;
pthread_attr_t attr;
pthread_attr_init(&attr);
size_t stack = (size_t)self->stackSize_;
if (stack >= PTHREAD_STACK_MIN) {
pthread_attr_setstacksize(&attr, stack);
}
pthread_create(&nt->t, &attr, &start_routine, [self retain]);
]-*/;
private void exit() {
state = State.TERMINATED;
if (threadGroup != null) {
threadGroup.threadTerminated(this);
threadGroup = null;
}
target = null;
uncaughtExceptionHandler = null;
interruptActions.clear();
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
private void rethrow(Throwable t) throws Throwable {
UncaughtExceptionHandler ueh = getUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(this, t);
} else {
throw t;
}
}
public static int activeCount() {
return currentThread().getThreadGroup().activeCount();
}
public boolean isDaemon() {
return isDaemon;
}
public void setDaemon(boolean isDaemon) {
this.isDaemon = isDaemon;
}
/**
* Prints to the standard error stream a text representation of the current
* stack for this Thread.
*
* @see Throwable#printStackTrace()
*/
public static void dumpStack() {
new Throwable("stack dump").printStackTrace();
}
public static int enumerate(Thread[] threads) {
Thread thread = Thread.currentThread();
return thread.getThreadGroup().enumerate(threads);
}
public long getId() {
return threadId;
}
public final String getName() {
return name;
}
public final void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name == null");
}
this.name = name;
}
public final int getPriority() {
return priority;
}
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();
}
synchronized (this) {
this.priority = newPriority;
if (isAlive()) {
nativeSetPriority(newPriority);
}
}
}
}
private native void nativeSetPriority(int priority) /*-[
struct sched_param param;
param.sched_priority = (priority * 64 / JavaLangThread_MAX_PRIORITY) - 1;
NativeThread *nt = (NativeThread *)self->nativeThread_;
pthread_setschedparam(nt->t, SCHED_OTHER, ¶m);
]-*/;
public State getState() {
return state;
}
public ThreadGroup getThreadGroup() {
return state == State.TERMINATED ? null : threadGroup;
}
public StackTraceElement[] getStackTrace() {
// Get the stack trace for a new exception, stripping the exception's
// and preamble's (runtime startup) frames.
StackTraceElement[] exceptionTrace = new Throwable().getStackTrace();
int firstElement = 0;
int lastElement = exceptionTrace.length;
for (int i = 0; i < exceptionTrace.length; i++) {
String methodName = exceptionTrace[i].getMethodName();
if (methodName.contains("getStackTrace")) {
firstElement = i;
continue;
}
if (methodName.contains("mainWithNSStringArray:")) {
lastElement = i;
break;
}
}
int nFrames = lastElement - firstElement + 1;
if (nFrames < 0) {
// Something failed, return the whole stack trace.
return exceptionTrace;
}
if (firstElement + nFrames > exceptionTrace.length) {
nFrames = exceptionTrace.length - firstElement;
}
StackTraceElement[] result = new StackTraceElement[nFrames];
System.arraycopy(exceptionTrace, firstElement, result, 0, nFrames);
return result;
}
@Deprecated
public int countStackFrames() {
return getStackTrace().length;
}
/**
* Posts an interrupt request to this {@code Thread}. Unless the caller is
* the {@link #currentThread()}, the method {@code checkAccess()} is called
* for the installed {@code SecurityManager}, if any. This may result in a
* {@code SecurityException} being thrown. The further behavior depends on
* the state of this {@code Thread}:
* <ul>
* <li>
* {@code Thread}s blocked in one of {@code Object}'s {@code wait()} methods
* or one of {@code Thread}'s {@code join()} or {@code sleep()} methods will
* be woken up, their interrupt status will be cleared, and they receive an
* {@link InterruptedException}.
* <li>
* {@code Thread}s blocked in an I/O operation of an
* {@link java.nio.channels.InterruptibleChannel} will have their interrupt
* status set and receive an
* {@link java.nio.channels.ClosedByInterruptException}. Also, the channel
* will be closed.
* <li>
* {@code Thread}s blocked in a {@link java.nio.channels.Selector} will have
* their interrupt status set and return immediately. They don't receive an
* exception in this case.
* <ul>
*
* @throws SecurityException
* if <code>checkAccess()</code> fails with a SecurityException
* @see java.lang.SecurityException
* @see java.lang.SecurityManager
* @see Thread#interrupted
* @see Thread#isInterrupted
*/
public void interrupt() {
synchronized(nativeThread) {
synchronized (interruptActions) {
for (int i = interruptActions.size() - 1; i >= 0; i--) {
interruptActions.get(i).run();
}
}
if (interrupted) {
return; // No further action needed.
}
interrupted = true;
if (blocker != null) {
synchronized(blocker) {
blocker.notify();
}
}
}
}
/**
* Returns a <code>boolean</code> indicating whether the current Thread (
* <code>currentThread()</code>) has a pending interrupt request (<code>
* true</code>) or not (<code>false</code>). It also has the side-effect of
* clearing the flag.
*
* @return a <code>boolean</code> indicating the interrupt status
* @see Thread#currentThread
* @see Thread#interrupt
* @see Thread#isInterrupted
*/
public static boolean interrupted() {
Thread currentThread = currentThread();
boolean result = currentThread.interrupted;
currentThread.interrupted = false;
return result;
}
/**
* Returns a <code>boolean</code> indicating whether the receiver has a
* pending interrupt request (<code>true</code>) or not (
* <code>false</code>)
*
* @return a <code>boolean</code> indicating the interrupt status
* @see Thread#interrupt
* @see Thread#interrupted
*/
public boolean isInterrupted() {
return interrupted;
}
/**
* Blocks the current Thread (<code>Thread.currentThread()</code>) until
* the receiver finishes its execution and dies.
*
* @throws InterruptedException if <code>interrupt()</code> was called for
* the receiver while it was in the <code>join()</code> call
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
public final void join() throws InterruptedException {
if (!isAlive()) {
return;
}
synchronized (nativeThread) {
while (isAlive()) {
nativeThread.wait(POLL_INTERVAL);
}
}
}
/**
* Blocks the current Thread (<code>Thread.currentThread()</code>) until
* the receiver finishes its execution and dies or the specified timeout
* expires, whatever happens first.
*
* @param millis The maximum time to wait (in milliseconds).
* @throws InterruptedException if <code>interrupt()</code> was called for
* the receiver while it was in the <code>join()</code> call
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
public final void join(long millis) throws InterruptedException {
join(millis, 0);
}
/**
* Blocks the current Thread (<code>Thread.currentThread()</code>) until
* the receiver finishes its execution and dies or the specified timeout
* expires, whatever happens first.
*
* @param millis The maximum time to wait (in milliseconds).
* @param nanos Extra nanosecond precision
* @throws InterruptedException if <code>interrupt()</code> was called for
* the receiver while it was in the <code>join()</code> call
* @see Object#notifyAll
* @see java.lang.ThreadDeath
*/
public final void join(long millis, int nanos) throws InterruptedException {
if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) {
throw new IllegalArgumentException("bad timeout: millis=" + millis + ",nanos=" + nanos);
}
// avoid overflow: if total > 292,277 years, just wait forever
boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI;
boolean forever = (millis | nanos) == 0;
if (forever | overflow) {
join();
return;
}
if (!isAlive()) {
return;
}
synchronized (nativeThread) {
if (!isAlive()) {
return;
}
// guaranteed not to overflow
long nanosToWait = millis * NANOS_PER_MILLI + nanos;
// wait until this thread completes or the timeout has elapsed
long start = System.nanoTime();
while (true) {
if (millis > POLL_INTERVAL) {
nativeThread.wait(POLL_INTERVAL);
} else {
nativeThread.wait(millis, nanos);
}
if (!isAlive()) {
break;
}
long nanosElapsed = System.nanoTime() - start;
long nanosRemaining = nanosToWait - nanosElapsed;
if (nanosRemaining <= 0) {
break;
}
millis = nanosRemaining / NANOS_PER_MILLI;
nanos = (int) (nanosRemaining - millis * NANOS_PER_MILLI);
}
}
}
public final boolean isAlive() {
State s = state;
return s != State.NEW && s != State.TERMINATED;
}
public void checkAccess() {
// Access checks not implemented on iOS.
}
public static void sleep(long millis) throws InterruptedException {
sleep(millis, 0);
}
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("millis < 0: " + millis);
}
if (nanos < 0) {
throw new IllegalArgumentException("nanos < 0: " + nanos);
}
if (nanos > 999999) {
throw new IllegalArgumentException("nanos > 999999: " + nanos);
}
// The JLS 3rd edition, section 17.9 says: "...sleep for zero
// time...need not have observable effects."
if (millis == 0 && nanos == 0) {
// ...but we still have to handle being interrupted.
if (Thread.interrupted()) {
throw new InterruptedException();
}
return;
}
Object lock = currentThread().nativeThread;
synchronized(lock) {
lock.wait(millis, nanos);
}
}
/**
* Causes the calling Thread to yield execution time to another Thread that
* is ready to run. The actual scheduling is implementation-dependent.
*/
public static native void yield() /*-[
pthread_yield_np();
]-*/;
private static synchronized long getNextThreadId() {
return threadOrdinalNum++;
}
/**
* Indicates whether the current Thread has a monitor lock on the specified
* object.
*
* @param object the object to test for the monitor lock
* @return true if the current thread has a monitor lock on the specified
* object; false otherwise
*/
public static native boolean holdsLock(Object object) /*-[
return j2objc_sync_holds_lock(object);
]-*/;
/**
* Returns the context ClassLoader for this Thread.
*
* @return ClassLoader The context ClassLoader
* @see java.lang.ClassLoader
*/
public ClassLoader getContextClassLoader() {
return contextClassLoader != null ? contextClassLoader : ClassLoader.getSystemClassLoader();
}
public void setContextClassLoader(ClassLoader cl) {
contextClassLoader = cl;
}
public String toString() {
ThreadGroup group = getThreadGroup();
if (group != null) {
return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]";
}
return "Thread[" + getName() + "," + getPriority() + ",]";
}
/**
* Unparks this thread. This unblocks the thread it if it was
* previously parked, or indicates that the thread is "preemptively
* unparked" if it wasn't already parked. The latter means that the
* next time the thread is told to park, it will merely clear its
* latent park bit and carry on without blocking.
*
* <p>See {@link java.util.concurrent.locks.LockSupport} for more
* in-depth information of the behavior of this method.</p>
*
* @hide for Unsafe
*/
public final void unpark$() {
Object vmt = nativeThread;
synchronized (vmt) {
switch (parkState) {
case ParkState.PREEMPTIVELY_UNPARKED: {
/*
* Nothing to do in this case: By definition, a
* preemptively unparked thread is to remain in
* the preemptively unparked state if it is told
* to unpark.
*/
break;
}
case ParkState.UNPARKED: {
parkState = ParkState.PREEMPTIVELY_UNPARKED;
break;
}
default /*parked*/: {
parkState = ParkState.UNPARKED;
vmt.notifyAll();
break;
}
}
}
}
/**
* Parks the current thread for a particular number of nanoseconds, or
* indefinitely. If not indefinitely, this method unparks the thread
* after the given number of nanoseconds if no other thread unparks it
* first. If the thread has been "preemptively unparked," this method
* cancels that unparking and returns immediately. This method may
* also return spuriously (that is, without the thread being told to
* unpark and without the indicated amount of time elapsing).
*
* <p>See {@link java.util.concurrent.locks.LockSupport} for more
* in-depth information of the behavior of this method.</p>
*
* <p>This method must only be called when <code>this</code> is the current
* thread.
*
* @param nanos number of nanoseconds to park for or <code>0</code>
* to park indefinitely
* @throws IllegalArgumentException thrown if <code>nanos < 0</code>
*
* @hide for Unsafe
*/
public final void parkFor$(long nanos) {
Object vmt = nativeThread;
synchronized (vmt) {
switch (parkState) {
case ParkState.PREEMPTIVELY_UNPARKED: {
parkState = ParkState.UNPARKED;
break;
}
case ParkState.UNPARKED: {
long millis = nanos / NANOS_PER_MILLI;
nanos %= NANOS_PER_MILLI;
parkState = ParkState.PARKED;
try {
vmt.wait(millis, (int) nanos);
} catch (InterruptedException ex) {
interrupt();
} finally {
/*
* Note: If parkState manages to become
* PREEMPTIVELY_UNPARKED before hitting this
* code, it should left in that state.
*/
if (parkState == ParkState.PARKED) {
parkState = ParkState.UNPARKED;
}
}
break;
}
default /*parked*/: {
throw new AssertionError("shouldn't happen: attempt to repark");
}
}
}
}
/**
* Parks the current thread until the specified system time. This
* method attempts to unpark the current thread immediately after
* <code>System.currentTimeMillis()</code> reaches the specified
* value, if no other thread unparks it first. If the thread has
* been "preemptively unparked," this method cancels that
* unparking and returns immediately. This method may also return
* spuriously (that is, without the thread being told to unpark
* and without the indicated amount of time elapsing).
*
* <p>See {@link java.util.concurrent.locks.LockSupport} for more
* in-depth information of the behavior of this method.</p>
*
* <p>This method must only be called when <code>this</code> is the
* current thread.
*
* @param time the time after which the thread should be unparked,
* in absolute milliseconds-since-the-epoch
*
* @hide for Unsafe
*/
public final void parkUntil$(long time) {
Object vmt = nativeThread;
synchronized (vmt) {
/*
* Note: This conflates the two time bases of "wall clock"
* time and "monotonic uptime" time. However, given that
* the underlying system can only wait on monotonic time,
* it is unclear if there is any way to avoid the
* conflation. The downside here is that if, having
* calculated the delay, the wall clock gets moved ahead,
* this method may not return until well after the wall
* clock has reached the originally designated time. The
* reverse problem (the wall clock being turned back)
* isn't a big deal, since this method is allowed to
* spuriously return for any reason, and this situation
* can safely be construed as just such a spurious return.
*/
long delayMillis = time - System.currentTimeMillis();
if (delayMillis <= 0) {
parkState = ParkState.UNPARKED;
} else {
parkFor$(delayMillis * NANOS_PER_MILLI);
}
}
}
/**
* Returns the default exception handler that's executed when uncaught
* exception terminates a thread.
*
* @return an {@link UncaughtExceptionHandler} or <code>null</code> if
* none exists.
*/
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
return defaultUncaughtHandler;
}
/**
* Sets the default uncaught exception handler. This handler is invoked in
* case any Thread dies due to an unhandled exception.
*
* @param handler
* The handler to set or null.
*/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
Thread.defaultUncaughtHandler = handler;
}
/**
* 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() {
UncaughtExceptionHandler h = uncaughtExceptionHandler;
return h != null ? h : threadGroup;
}
/**
* 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;
}
private static class SystemUncaughtExceptionHandler implements UncaughtExceptionHandler {
@Override
public synchronized void uncaughtException(Thread t, Throwable e) {
System.err.print("Exception in thread \"" + t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
/**
* Adds a runnable to be invoked upon interruption. If this thread has
* already been interrupted, the runnable will be invoked immediately. The
* action should be idempotent as it may be invoked multiple times for a
* single interruption.
*
* <p>Each call to this method must be matched with a corresponding call to
* {@link #popInterruptAction$}.
*
* @hide used by NIO
*/
public final void pushInterruptAction$(Runnable interruptAction) {
synchronized (interruptActions) {
interruptActions.add(interruptAction);
}
if (interruptAction != null && isInterrupted()) {
interruptAction.run();
}
}
/**
* Removes {@code interruptAction} so it is not invoked upon interruption.
*
* @param interruptAction the pushed action, used to check that the call
* stack is correctly nested.
*
* @hide used by NIO
*/
public final void popInterruptAction$(Runnable interruptAction) {
synchronized (interruptActions) {
Runnable removed = interruptActions.remove(interruptActions.size() - 1);
if (interruptAction != removed) {
throw new IllegalArgumentException(
"Expected " + interruptAction + " but was " + removed);
}
}
}
/**
* Returns a map of stack traces for all live threads.
*/
// TODO(user): Can we update this to return something useful?
public static Map<Thread,StackTraceElement[]> getAllStackTraces() {
return Collections.<Thread, StackTraceElement[]>emptyMap();
}
@Deprecated
public final void stop() {
stop(new ThreadDeath());
}
@Deprecated
public final void stop(Throwable obj) {
throw new UnsupportedOperationException();
}
@Deprecated
public void destroy() {
throw new UnsupportedOperationException();
}
@Deprecated
public final void suspend() {
throw new UnsupportedOperationException();
}
@Deprecated
public final void resume() {
throw new UnsupportedOperationException();
}
}