Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4010086

Thread.stop(Throwable obj) can be caught, but thead apparently marked as dead

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • 1.1
    • 1.0.2
    • core-libs
    • None
    • 1.1
    • sparc
    • solaris_2.5
    • Not verified

      Compile and run the enclosed file as a Java application.
      I have annotated the application trace output with lines marked ">> ".

      The spec says that this "is an unusual action to take". If you consider
      the example code wrong-headed, the threads implementation is still strange.
      It unwinds the computation partway and lets the thread continue to run outside
      the "catch" clause. Then it terminates without executing the "finally" clause,
      which should run according to the language specification.

      3 clam% java ActiviTest
      Action thread ready for request.
      >> Press the "compute" button.
      Application issuing request.
      Application request issued.
      Application showing killer dialog.
      Action thread got request ActiviTest@ee700210
      Sleeping.
      Application notified, end of ActiviTest@ee700210
      Application popping down monitor window.
      Action thread completed request.
      Action thread ready for request.
      >> Press the "compute" button again.
      Application issuing request.
      Application request issued.
      Application showing killer dialog.
      Action thread got request ActiviTest@ee7003e0
      Sleeping.
      >> Press the "Abort" button.
      Killer dialog requesting abort of ActiviTest@ee7003e0
      Stop returned to caller.
      Application notified, end of ActiviTest@ee7003e0
      Application popping down monitor window.
      >> At this point in the output there is a delay as the sleep completes
      >> its full specified time.
      Application notified, end of ActiviTest@ee7003e0
      >> Notice that the action thread continues running and waits
      >> for the next request.
      Action thread caught abort.
      Action thread ready for request.
      >> Press the "compute" button again.
      Application issuing request.
      >> The event handler acquires the lock on the action thread's queue
      >> then checks to see if it is alive. I added this check because
      >> the action thread was not responding.
      Action thread is dead.
      >> Note that the action thread never indicates that it is dying
      >> even though there is a "finally" clause in its main loop and
      >> the language specification states that "finally" clauses execute
      >> when threads die.

      Starting new action thread.



      ////////////////////////////////////////////////////////////////////////////
      //
      // %W% %G% %U%
      //
      // Copyright %G% Sun Microsystems, Inc. All Rights Reserved
      //
      ////////////////////////////////////////////////////////////////////////////

      // package COM.sun.share.ejava.test;
      import java.util.*;
      import java.awt.*;
      import java.io.*;



      /** A sample activity that takes some time.
       */
      public class ActiviTest extends Activity {


      public void act() {
          System.err.println("Sleeping.");
          try {
      Thread.sleep(5000);
          } catch(InterruptedException e) {
      System.err.println("InterrunptedException.");
          }
          /*
          System.err.print("Read> ");
          try {
      System.out.println("Read: " + new DataInputStream(System.in).readLine());
          } catch(IOException e) {
      System.err.println("I/O Exception.");
          }
          */
      }


      public static void main(String[] args) {
          TestFrame f = new TestFrame();
          f.pack();
          f.show();
      }

      }



      ////////////////////////////////////////////////////////////////////////////

      class TestFrame extends Frame implements Observer {

      private Activity act;
      Dialog killer;


      public TestFrame() {
          setTitle("Java Killer App");
          Panel panel = new Panel();
          add("Center", panel);
          panel.add(new Button("Press to compute."));
      }


      public boolean action(Event e, Object what) {
          act = new ActiviTest();
          act.setObserver(this);
          killer = new Killer(this, act);
          System.err.println("Application issuing request.");
          act.request();
          System.err.println("Application request issued.");
          System.err.println("Application showing killer dialog.");
          killer.show();
          return true;
      }


      public void update(Observable obs, Object arg) {
          System.err.println("Application notified, end of " + arg);
          if (arg==act) {
      act = null;
      System.err.println("Application popping down monitor window.");
      killer.hide();
          }
      }

      }



      ////////////////////////////////////////////////////////////////////////////

      class Killer extends Dialog {

      private Activity act;
      public Label message;


      public Killer(Frame f, Activity a) {
          super(f, "Java App Killer", false); // Should be "true".
          act = a;
          add("South", new Button("Abort"));
          message = new Label("Press to Terminate");
          add("Center", message);
          pack();
      }


      public boolean action(Event e, Object what) {
          if (!act.isActive()) {
      System.err.println("Killer dialog: not active " + act);
      // Hide this window right away; don't wait for
      // the activity to get unstuck.
      hide();
          }
          System.err.println("Killer dialog requesting abort of " + act);
          act.abort();
          return true;
      }


      }


      ////////////////////////////////////////////////////////////////////////////

      /** This class provides a concept of an activity with a beginning,
        a method that carries out the activity, and an end. The activity can be
        aborted by request, and this class generates and handles the associated
        exception. An Activity has active and inactive states. The class provides
        synchronization to consistently manage the state transitions. If an Observer
        is designated, this class notifies the Observer at the point where
        the activity terminates normally or is aborted. Activities can be queued
        for execution, and this base class provides a default background thread dedicated
        to performing Activities.
        */
      abstract class Activity {

      private static BasicActionThread defaultThread = new BasicActionThread();
      private boolean active;
      private Thread thread;
      private Observer observer;


      public Activity() {}

      /** Establishes an Observer to be notified of termination.
       */
      public void setObserver(Observer o) {
          observer = o;
      }


      /** Tells whether the Activity is still in progress, i.e. is not terminated.
       */
      public boolean isActive() { return active; }


      /** This method invokes the activity in the current thread, calling
        begin(), then act(), then end() within a "finally" clause. This
        method returns immediately in case of an abort, allowing only end()
        and any onAbort code to run.
       */
      public void perform() {
          try {
      // System.err.println("Beginning.");
      begin();
      // System.err.println("Doing act.");
      act();
      // System.err.println("Finished act.");
          } finally {
      // System.err.println("Doing end.");
      end();
          }
      }


      /** Override this method to do the work of the activity.
        Typically, perform() gets called because the application code called
        request(). In this case, expect act() to not run in the thread that
        called request().
        */
      protected abstract void act();


      /** Any action desired in the action thread in case of an Abort.
        Does nothing in the base class.
       */
      protected void onAbort() {}


      /** Called in the action thread to enter the active state. A subclass
        can add actions to immediately precede entry to the active state.
        */
      protected synchronized void begin() {
          if (active) throw new RuntimeException("Activity is already active.");
          thread = Thread.currentThread();
          active = true;
      }


      /** Called exactly once as the Activity is ending, whether
        normally or by being stopped (aborted). Calls any Observer's update
        method, passing null as the Observable and this Activity as the
        second argument. Possibly could be called twice if abort is called
        in the activity thread.
        */
      protected synchronized void end() {
          boolean was = active;
          active = false;
          if (observer!=null && was) {
      observer.update(null, this);
          }
      }


      /** Call this to immediately terminate the activity. If the activity
        has not yet terminated, stops it, causing end() to run in the action
        thread.
        */
      public synchronized void abort() {
          if (active) {
      thread.stop(new AbortActivity());
      System.err.println("Stop returned to caller.");
      if (observer!=null) observer.update(null, this);
      if (thread instanceof BasicActionThread)
      ((BasicActionThread)thread).isStuck = true;
          }
      }


      /** This method causes the perform() method to eventually be invoked
        in an action thread. The base class provides a single action thread
        and no queueing of requests. If the action thread is performing an
        activity, calls to the base class request() method block until the
        activity terminates.
        */
      public void request() {
          synchronized (defaultThread) {
      if (!defaultThread.isAlive() || defaultThread.isStuck) {
      if (defaultThread.isStuck) System.err.println("Action thread is stuck.");
      else System.err.println("Action thread is dead.");
      defaultThread = new BasicActionThread();
      System.err.println("\nStarting new action thread.\n");
      defaultThread.start();
      }
      defaultThread.putRequest(this);
          }
      }


      static {
          defaultThread.start();
      }

      }


      ////////////////////////////////////////////////////////////////////////////


      /** A basic kind of thread that blocks requestors until it is ready
        to begin the next Activity.
        */
      class BasicActionThread extends Thread {

      private Activity act;

      // This should be true between the time the thread has
      // been asked to abort and the time it is ready to process another request.
      public boolean isStuck = false;



      public void run() {
          try {
      while (!isStuck) {
      try {
      System.err.println("Action thread ready for request.");
      Activity a = getRequest();
      System.err.println("Action thread got request " + a);
      a.perform();
      System.err.println("Action thread completed request.");
      // ?? isStuck = false;
      } catch(AbortActivity e) {
      System.err.println("Action thread caught abort.");
      isStuck = false;
      }

      }
          } finally { System.err.println("Action thread dying."); }
      }


      public synchronized Activity getRequest() {
          while (act==null) { justWait(); }
          Activity a = act;
          act = null;
          notify();
          return a;
      }


      public synchronized void putRequest(Activity a) {
          while (act!=null) { justWait(); }
          act = a;
          notify();
      }


      public void justWait() {
          try {
      wait();
          } catch(InterruptedException e) {
      System.err.println("Interrupted.");
          }
      }

      }



      ////////////////////////////////////////////////////////////////////////////

      /** Internal Exception class.
       */
      class AbortActivity extends RuntimeException {}


            tlindholsunw Timothy Lindholm (Inactive)
            duke J. Duke
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: