Source code for /engineering/autohit-1998/autohit/vm/VMContext.javaOriginal file VMContext.java
   1 /**
   2  * .
   3  * Copyright � 1999 Erich P G.
   4  *
   5  */
   6  
   7 package autohit.vm;
   8 
   9 import autohit.utils.ObjectMonitor;
  10 
  11 /**
  12  * A VM context.  It will wrap a VM in a thread and do all the
  13  * thread-safe kinda stuff.  A context will handle successive
  14  * VM runs, so you can use them in a thread pool.
  15  * <p>
  16  * BE SURE to .start() this Thread BEFORE any other threads
  17  * access its methods (particularly execute()).  Failure to heed this
  18  * warning COULD result in a race condition...  :-)
  19  * <p>
  20  * @author Erich P. Gatejen
  21  * @version 1.0
  22  * <i>Version History</i>
  23  * <code>EPG - Initial - 19Jan99 
  24  * EPG - Fixed bug. - 19Fef99  
  25  *    - This thread doesn't need to be daemonized.  It seems it wasn't dying properly
  26  *      because of a rather stupid bug.  I'll leave it a daemon for now, just cause.
  27  * </code>
  28  */
  29 public class VMContext extends Thread {
  30 	
  31 	// --- FINAL FIELDS ------------------------------------------------------	
  32      
  33 	// --- FIELDS ------------------------------------------------------------
  34 
  35     /**
  36      *  A runnable VM.  This will be a fully implemented derived-class
  37      *  of VM.
  38      */      	    
  39      protected VM       rVM;
  40      
  41     /**
  42      *  An object monitor for blocking.
  43      */      	    
  44      private ObjectMonitor   vsBlock;
  45      
  46     /**
  47      *  Alive.  true means we should keep goin'
  48      */      	    
  49      private boolean  alive;           
  50 
  51     /**
  52      *  Request pause
  53      */      	    
  54      private boolean  reqPause;  
  55 
  56     /**
  57      *  Request stop
  58      */      	    
  59      private boolean  reqStop;
  60      
  61     /**
  62      *  Request resume
  63      */      	    
  64      private boolean  reqResume;
  65           
  66     /**
  67      *  Request kill
  68      */      	    
  69      private boolean  reqKill;
  70     
  71 	// --- PUBLIC METHODS ----------------------------------------------------	
  72 
  73 
  74     /**
  75      *  Constructor.
  76      */      
  77      public VMContext() {
  78 
  79           // Make this when we run.  otherwise, we wont own it,
  80           vsBlock = null;
  81 
  82           // be paranoid
  83           rVM     = null;
  84           alive   = true;
  85           reqKill = false;
  86           
  87           this.setDaemon(true);             
  88      }
  89 
  90     /**
  91      *  Load and Execute a VM.  If a VM is already running, it will return 
  92      *  false.  Otherwise, it will return true.
  93      *
  94      *  This method can be called by any thread.
  95      *
  96      *  @param aVM A fully implimented derived-class of VM.
  97      *  @return true if successful and execution begun, false if
  98      *               another VM is already running.
  99      *
 100      *  @see autohit.vm.VM    
 101      */  
 102      public synchronized boolean execute(VM  aVM) {
 103 
 104           if (rVM != null) return false;
 105 
 106           rVM = aVM;
 107 
 108           // notify() this Thread Object in case it has
 109           // blocked itself.  Prolly could use notify(), but why risk
 110           // it.
 111           this.notifyAll();
 112           return true;          
 113      }
 114 
 115     /**
 116      *  Pause the vm.  This may be called by another thread.
 117      *
 118      *  As with vmResume() and vmStop(), it posts a request to the
 119      *  VMContext.  The context will not heed the request until
 120      *  the current VM.execute() is complete and the context has
 121      *  a chance to check for these requests.
 122      *  <p>
 123      *  There is no rendezvous; the method will not block.  All successive
 124      *  requests count as the same request until the context services
 125      *  it.  There is no guarentee on the timing of the service.  The Context
 126      *  will not check for requests until AFTER the current instruction is complete.
 127      *  So, if the vm is executing a long wait instruction, it could indeed be
 128      *  some time before the request is serviced.
 129      *  <p>
 130      *  If you need to make sure that the request worked, then use
 131      *  the verifyState() method to get the definative state of the
 132      *  VM/Context.
 133      *  <p>
 134      *  As for the return value, "success" merely means that the
 135      *  request was successfully posted and not that the action
 136      *  was completed.
 137      *  <p>
 138      *  One last thaught: this is not a robust OS implimentation of
 139      *  a thread context.  You might want to restrict calls to vmPause()
 140      *  and vmResume() to a single external thread.  Multiple threads
 141      *  might get confused if they don't cooperate...
 142      *
 143      *  @return true is successful.  false if no vm is running or
 144      *               it is already paused.
 145      *  @see autohit.vm.VM    
 146      */  
 147      public boolean vmPause() {
 148           if (rVM.getState() == VM.STATE_RUNNING) {
 149                reqPause = true;
 150                return true;
 151           } else return false;
 152      }     
 153 
 154     /**
 155      *  Resume the vm.
 156      *  <p>
 157      *  See the notes for the pause() method.
 158      *
 159      *  @return true is successful.  false if no vm is paused or
 160      *               it is already running.
 161      *  @see autohit.vm.VM    
 162      */  
 163      public boolean vmResume() {
 164           if (rVM.getState() == VM.STATE_PAUSED) {
 165                reqResume = true;
 166                return true;
 167           } else return false;          
 168      }
 169      
 170     /**
 171      *  Stop the vm.  This will kill it permanently, so be careful.
 172      *  <p>
 173      *  See the notes for the pause() method.
 174      *
 175      *  @return true is successful.  false if no vm is running or
 176      *               paused.
 177      *  @see autohit.vm.VM    
 178      */  
 179      public boolean vmStop() {
 180           if (rVM == null) return false;
 181           else {
 182                reqStop = true;
 183                return true;
 184           } 
 185      }
 186 
 187     /**
 188      *  Kill this context.  This is a request, so it may not happen
 189      *  immeadiately.  Once it does, it is irrevocable as the Thread
 190      *  run() method will be allowed to return...
 191      *  <p>
 192      *  See the notes for the pause() method.
 193      *
 194      *  @return always returns true.
 195      *  @see autohit.vm.VM    
 196      */  
 197      public synchronized boolean kill() {
 198           reqKill = true;
 199           alive   = false;         
 200           this.notify();  // in case it is waiting for execute().         
 201           return true;
 202      }
 203      
 204     /**
 205      *  Verify the state of the VM.  It will report a VM state 
 206      *  value as defined in the VM class--VM.State_*.  This will
 207      *  be the authorative state, as this method blocks until the
 208      *  VM has chance to clear requests and unblock it.
 209      *  <p>
 210      *  (And, never EVER call this method from within THIS thread.
 211      *   You'll almost certainly deadlock it.)
 212      *  <p>
 213      *  The following describes each state:
 214      *  <pre>
 215      *         STATE_NEW          = VM is loaded bu not started
 216      *         STATE_RUNNING      = VM is actively running.
 217      *         STATE_PAUSED       = VM is paused.
 218      *         STATE_DONE         = VM finished execution.
 219      *                              This is rare, as the VM will
 220      *                              be automatically unloaded when
 221      *                              finished.
 222      *         STATE_NO_VM        = No VM is loaded into this context.
 223      *  </pre>
 224      * 
 225      *  @return a VM.State_* value.
 226      *  @see autohit.vm.VM    
 227      */  
 228      public int verifyState() {
 229         
 230         try {  
 231             vsBlock.ownWait();
 232         } catch (Exception  e) {
 233             // Dont care if we are interrupted.    
 234         }
 235         return this.getState();  
 236      }                
 237 
 238     /**
 239      *  A simple request for state.  It may or not be stale be the
 240      *  time the calling thread gets it.  If you msut have THE
 241      *  AUTHORATIVE state, then call verifyState()
 242      *  
 243      *  @return a VM.State_* value.
 244      *  @see autohit.vm.VM    
 245      */  
 246      public int getState() {
 247            
 248            if (rVM == null) return VM.STATE_NO_VM;         
 249            else return rVM.getState();
 250      }      
 251 
 252     /**
 253      *  Run the context
 254      */  
 255      public void run() {
 256           
 257           // Make this so that it that is is owned by this thread.
 258           vsBlock = new ObjectMonitor();          
 259 
 260           // Ok, there is a SLIGHT danger of a race condition here,
 261           // if someone was able to call execute() after construction
 262           // but before the this Thread is .start()'ed.
 263           
 264           // We shouldn't have anything to do but wait for a VM
 265           // to execute.
 266           blockTillExecute();
 267           
 268           // Varying VM loop  (19Feb -- what the hell does "varying" mean?)
 269           do {
 270 
 271                // Any VM exception will make the context
 272                // dump the VM.  This will also catch the NullPointerException
 273                // that would be caused by a spurious context thread unblock when
 274                // a VM has not be set for execution.
 275                try {
 276             
 277                     // Clear the VM requests...  don't care if there are any
 278                     // pending.
 279                     reqPause  = false;
 280                     reqStop   = false;
 281                     reqResume = false;
 282                    
 283                     // Start the VM
 284                     rVM.start();
 285                     
 286                     // Specific VM loop
 287                     do {
 288                                  
 289                          // Stablize state and unblock all waiting for verifyState()
 290                          if ((reqKill == true)||(reqStop == true)) {
 291                               // alive = false is already set if reqKill                              
 292                               vsBlock.ownNotifyAll();
 293                               break;                        
 294                          }
 295                          if (reqPause == true) {
 296                               rVM.pause();    
 297                          }
 298                          if (reqResume == true) {
 299                               rVM.resume();
 300                          }                                     
 301                          vsBlock.ownNotifyAll();
 302                         
 303                          // Execute an instruciton.  An exception
 304                          // kills this VM
 305                          rVM.execute();             
 306                         
 307                     } while (alive == true);  // EPG 18Feb99 I can't beleive I coded this as (alive = true).  This is what
 308                                               // was keeping the context from dying properly...
 309                
 310                } catch (Exception e) {
 311                     // Don't actuall have to do anything...
 312                } 
 313                               
 314                // The VM will die now.
 315                rVM = null;
 316                vsBlock.ownNotifyAll();                            
 317                
 318                // Before we sleep, lets make sure a Kill request
 319                // didnt arrive
 320                if (alive == false) break;
 321                blockTillExecute(); 
 322           
 323           } while (alive == true);  // EPD 18Feb99 Did the same dumb trick here, too.
 324           
 325           // Last out the door turn off the lights...
 326           // DO NOT put ANY other code beyond the following statements
 327           // or you are liable to deadlock other Threads.
 328           vsBlock.ownNotifyAll();
 329           yield();
 330      }
 331      
 332 	// --- PRIVATE METHODS ---------------------------------------------------	
 333 
 334     /**
 335      *  Wait until someone calls our execute() method.
 336      */  
 337      public void blockTillExecute() {
 338           
 339         try {
 340             wait();          
 341         } catch (Exception e) {
 342             // Don't care if we are interrupted...
 343         }
 344 
 345      }
 346      
 347 
 348 }