view src/classes/java/lang/Thread.java @ 34:49be04cc6389 default tip java9-try

cyclic dependency ...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 19 Dec 2017 11:21:23 +0900
parents 61d41facf527
children
line wrap: on
line source

/*
 * 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;
}