import java.awt.*;
import java.applet.Applet;

public class ThreadJoinFix extends Applet implements Runnable {
  Button _startThreadButton;
  TextField _threadTimeToRun;
  TextField _threadTimeOut;
  TextField _timeWaited;
  int _timeToSleep = 100;

  // THIS PART CHANGES:
  // We can't use isAlive() method on
  // Thread because of RACE CONDITION!
  boolean _isThreadReallyAlive = false;

  public void init() { 
    setLayout(new BorderLayout());
  
    _startThreadButton = new Button("Start Running The Thread");
    add("Center",_startThreadButton);

    Panel insidePanel = new Panel();
    add("South",insidePanel);
    
    insidePanel.setLayout(new GridLayout(3,2));
    insidePanel.add(new Label("Time in ms for Thread to run:"));
    _threadTimeToRun = new TextField("2000");
    insidePanel.add(_threadTimeToRun);
    
    insidePanel.add(new Label("Time in ms before TimeOut:"));
    _threadTimeOut = new TextField("3000");
    insidePanel.add(_threadTimeOut);
   
    insidePanel.add(new Label("Time Join Waited:"));
    _timeWaited = new TextField("");
    insidePanel.add(_timeWaited);
         
  }

  private void startThreadClicked() {
    Thread t = new Thread(this);
    int timeOut = Integer.parseInt(_threadTimeOut.getText());
    long startTime = System.currentTimeMillis();
    _timeWaited.setText("");
    _isThreadReallyAlive = true;
    t.start();

    // THIS PART CHANGES:
    // Instead of t.join() we use
    // our own routine
    long base = System.currentTimeMillis();
    long delay = 0;
    synchronized(this) {
      try {
        while(_isThreadReallyAlive) {
          delay = timeOut - (System.currentTimeMillis() - base);
          if (delay <= 0) break;
          wait (delay);
        }
      } catch (InterruptedException e) {}
    } // End synchronized block

    long stopTime = System.currentTimeMillis();
    t.stop();
    _timeWaited.setText((new Integer((int)(stopTime-startTime))).toString());
  }

  public boolean handleEvent(Event event) {
    if (event.target == _startThreadButton && event.id == Event.ACTION_EVENT) {
      startThreadClicked();
      return true;
    }
    return super.handleEvent(event);
  }

  public void run() {
    int timeForThreadToRun = Integer.parseInt(_threadTimeToRun.getText());
    int timesToRun = timeForThreadToRun / _timeToSleep;
    System.out.println("Times To Run: " + 
      (new Integer(timesToRun)).toString());
    for (int i = 0; i < timesToRun; i++) {
      System.out.println( (new Integer(i * _timeToSleep)).toString());
      try {
        Thread.sleep(_timeToSleep);
      } catch (InterruptedException e) {}
    }

    // THIS PART CHANGES:
    // We have to notify blocking thread that 
    // we have basically ended
    //
    // Sets instance variable to let routine
    // know the thread is basically about to die.
    synchronized(this) { _isThreadReallyAlive = false; notifyAll(); }
  } // End of run()


} // end of class