diff src/classes/java/lang/Thread.java @ 0:61d41facf527

initial v8 import (history reset)
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Fri, 23 Jan 2015 10:14:01 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/java/lang/Thread.java	Fri Jan 23 10:14:01 2015 -0800
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed 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 gov.nasa.jpf.annotation.NeverBreak;
+import sun.nio.ch.Interruptible;
+
+/**
+ * MJI model class for java.lang.Thread library abstraction
+ * 
+ * <2do> this should not require the JPF ThreadList to retrieve corresponding ThreadInfos
+ * (the ThreadList might not store terminated threads)
+ */
+public class Thread implements Runnable {
+
+  public interface UncaughtExceptionHandler {
+    // note this doesn't stop the thread from being terminated
+    void uncaughtException (Thread t, Throwable x);
+  }
+  
+  static int nameThreadNum; // to construct the default thread name  
+
+  public static final int MIN_PRIORITY = 1;
+  public static final int NORM_PRIORITY = 5;
+  public static final int MAX_PRIORITY = 10;
+
+  // don't rename this - it's used by ThreadGoup.uncaughtException()
+  private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; // null by default
+  
+  // initialized in init(), except of the main thread (which gets explicitly initialized by the VM)
+  ThreadGroup group;
+  Runnable target;
+  String name;
+  int priority;
+  boolean isDaemon;
+  
+  // this is an explicit thread state that gets set on a call of interrupt(), but
+  // only if the thread is not blocked. If it is, we only change the status.
+  // this gets cleared by calling interrupted()
+  boolean             interrupted;
+  
+  // those are only accessed from peers since thread obects are per se shared
+  @NeverBreak
+  ThreadLocal.Entry<?>[] threadLocals;
+  
+  // this is what we use for sun.misc.Unsafe.park()/unpark()
+  // this is accessed from the native peer, VM.createMainThread() and sun.misc.Unsafe
+  static class Permit {
+    boolean blockPark = true; // this is used to remember unpark() calls before park() (they don't accumulate)
+  }
+  Permit permit; // the object is used for wait/notify
+
+  // referenced by java.util.concurrent.locks.LockSupport via sun.misc.Unsafe
+  // DON'T CHANGE THIS NAME
+  volatile Object parkBlocker;
+
+  // used to store Thread.stop() exceptions
+  Throwable stopException;
+  
+  private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null by default
+
+  
+  public enum State { BLOCKED, NEW, RUNNABLE, TERMINATED, TIMED_WAITING, WAITING }
+
+  
+  public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler xh) {
+    defaultUncaughtExceptionHandler = xh;
+  }
+  
+  public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
+    return defaultUncaughtExceptionHandler;
+  }
+  
+  
+  public Thread () {
+    this(null, null, null, 0L);
+  }
+
+  public Thread (Runnable target) {
+    this(null, target, null, 0L);
+  }
+
+  public Thread (Runnable target, String name) {
+    this(null, target, name, 0L);
+  }
+
+  public Thread (String name) {
+    this(null, null, name, 0L);
+  }
+
+  public Thread (ThreadGroup group, String name) {
+    this(group, null, name, 0L);
+  }
+  
+  public Thread (ThreadGroup group, Runnable target) {
+    this(group, target, null, 0L);
+  }
+
+  public Thread (ThreadGroup group, Runnable target, String name) {
+    this(group, target, name, 0L);
+  }
+
+  public Thread (ThreadGroup group, Runnable target, String name, long stackSize) {
+    Thread cur = currentThread();
+
+    if (group == null) {
+      this.group = cur.getThreadGroup();
+    } else {
+      this.group = group;
+    }
+
+    this.group.add(this);
+
+    if (name == null) {
+      this.name = "Thread-" + ++nameThreadNum;
+    } else {
+      this.name = name;
+    }
+
+    this.permit = new Permit();
+
+    // those are always inherited from the current thread
+    this.priority = cur.getPriority();
+    this.isDaemon = cur.isDaemon();
+
+    this.target = target;
+
+    // do our associated native init
+    init0(this.group, target, this.name, stackSize);
+    
+    initThreadLocals(cur);
+  }
+
+  // this takes care of ThreadInfo initialization
+  native void init0 (ThreadGroup group, Runnable target, String name, long stackSize);
+  
+  // this is here since InheritableThreadLocals would require childValue(parentVal) roundtrips.
+  // Unfortunately we can't defer this until the ThreadLocal is actually accessed since
+  // we have to capture the value at the point of child creation
+  // Note this executes in the parent thread
+  private void initThreadLocals (Thread parent){
+    ThreadLocal.Entry<?>[] tl = parent.threadLocals;
+    if (tl != null){
+      int len = tl.length;
+      ThreadLocal.Entry<?>[] inherited = null;
+      int j=0;
+      
+      for (int i=0; i<len; i++){
+        ThreadLocal.Entry<?> e = tl[i];
+        ThreadLocal.Entry<?> ec = e.getChildEntry();
+        if (ec != null){
+          if (inherited == null){
+            inherited = new ThreadLocal.Entry<?>[len];
+          }
+          inherited[j++] = ec;
+        }
+      }
+      
+      if (inherited != null){
+        ThreadLocal.Entry<?>[] a = new ThreadLocal.Entry<?>[j];
+        System.arraycopy(inherited,0,a,0,j);
+        threadLocals = a;
+      }
+    }
+  }
+  
+  public static int activeCount () {
+    return 0;
+  }
+
+  public void setUncaughtExceptionHandler(UncaughtExceptionHandler xh) {
+    uncaughtExceptionHandler = xh;
+  }
+  
+  public UncaughtExceptionHandler getUncaughtExceptionHandler(){
+    if (uncaughtExceptionHandler != null){
+      return uncaughtExceptionHandler;
+    } else {
+      return group;
+    }
+  }
+  
+  public void setContextClassLoader (ClassLoader cl) {
+  }
+
+  public ClassLoader getContextClassLoader () {
+    // <NSY>
+    return null;
+  }
+
+  public synchronized void setDaemon (boolean isDaemon) {
+    this.isDaemon = isDaemon;
+    setDaemon0(isDaemon);
+  }
+
+  public boolean isDaemon () {
+    return isDaemon;
+  }
+
+  public native long getId();
+
+  public StackTraceElement[] getStackTrace() {
+    return null; // not yet implemented
+  }
+
+  public native int getState0();
+
+  public Thread.State getState() {
+    int i = getState0();
+    switch (i) {
+    case 0: return State.BLOCKED;
+    case 1: return State.NEW;
+    case 2: return State.RUNNABLE;
+    case 3: return State.TERMINATED;
+    case 4: return State.TIMED_WAITING;
+    case 5: return State.WAITING;
+    }
+
+    return null; // shoudl be intercepted by a getState0 assertion
+  }
+
+  public synchronized void setName (String name) {
+    if (name == null) {
+      throw new IllegalArgumentException("thread name can't be null");
+    }
+
+    this.name = name;
+    setName0(name);
+  }
+
+  public String getName () {
+    return name;
+  }
+
+  public void setPriority (int priority) {
+    if ((priority < MIN_PRIORITY) || (priority > MAX_PRIORITY)) {
+      throw new IllegalArgumentException("thread priority out of range");
+    }
+
+    this.priority = priority;
+    setPriority0(priority);
+  }
+
+  public int getPriority () {
+    return priority;
+  }
+
+  public ThreadGroup getThreadGroup () {
+    return group;
+  }
+
+  public void checkAccess () {
+    // <NSY>
+  }
+
+  public native int countStackFrames ();
+
+  public static native Thread currentThread ();
+
+  public void destroy () {
+  }
+
+  public static void dumpStack () {
+  }
+
+  public static int enumerate (Thread[] tarray) {
+    Thread cur = currentThread();
+
+    return cur.group.enumerate(tarray);
+  }
+
+  public static native boolean holdsLock (Object obj);
+
+  // this one needs to be native because it might change the thread status
+  public native void interrupt ();
+
+  // those don't have to be native, but we keep it symmetric
+  public static native boolean interrupted ();
+  public native boolean isInterrupted ();
+
+  public native boolean isAlive ();
+
+
+  /**
+   * note these are not synchronized anymore since they are intercepted by the
+   * native peer. The reason is that we don't want two CGs per join call (one for the
+   * sync call, and one for the wait) because this can cause serious
+   * performance degradation
+   */
+  public void join () throws InterruptedException {
+    synchronized(this){
+
+      if (interrupted()) {
+        throw new InterruptedException();
+      }
+
+      while (isAlive()) {
+        // apparently, the JDK doesn't throw InterruptedExceptions if
+        // we get interrupted after waiting in the join
+        wait();
+      }
+    }
+  }
+
+  public void join (long millis) throws InterruptedException {
+    join(millis, 0);
+  }
+
+  public void join (long millis, int nanos) throws InterruptedException {
+
+    if (millis < 0){
+      throw new java.lang.IllegalArgumentException("timeout value is negative");
+
+    } else if (millis == 0){
+      join();
+
+    } else {
+      synchronized(this){
+        if (interrupted()){
+          throw new InterruptedException();
+        }
+
+        wait(millis);
+      }
+    }
+  }
+
+    
+
+  @Override
+  public void run () {
+    if (target != null) {
+      target.run();
+    }
+  }
+
+  public static void sleep (long millis) throws InterruptedException {
+    sleep(millis, 0);
+  }
+
+  public static native void sleep (long millis, int nanos)
+                            throws InterruptedException;
+
+  public native void start();
+  public native void stop();
+  public native void stop(Throwable obj);
+
+  public native void suspend();
+  public native void resume();
+
+
+  @Override
+  public String toString () {
+    return ("Thread[" + name + ',' + priority + ',' + (group == null ? "" : group.getName()) + ']');
+  }
+
+  public static native void yield ();
+
+  native void setDaemon0 (boolean on);
+
+  native void setName0 (String name);
+
+  native void setPriority0 (int priority);
+
+
+
+  /**
+   * automatically called by system upon thread termination to clean up
+   * references.
+   * 
+   * NOTE - we clean up atomically during ThreadInfo.finish(), to avoid any
+   * additional states. This is important since group per se is a shared object
+   * We only include this method here as a specification for ThreadInfo
+   */
+  private void exit () {
+    if (group != null){
+      group.threadTerminated(this);
+      group = null;
+    }
+    
+    threadLocals = null;    
+    parkBlocker = null;
+    uncaughtExceptionHandler = null;
+  }
+
+  // some Java 6 mojo
+  // <2do> not implemented yet
+  native void blockedOn (Interruptible b);
+
+  
+  // we probably will remove these fields once we modeled java.util.concurrent.ThreadLocalRandom 
+  // to make it deterministic
+  long threadLocalRandomSeed;
+  int threadLocalRandomProbe;
+  int threadLocalRandomSecondarySeed;
+}