Source code for /engineering/autohit-2003/src/autohit/vm/VMProcessAutomat.javaOriginal file VMProcessAutomat.java
   1 /**
   2  * AUTOHIT 2003
   3  * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
   4  * 
   5  * This program is free software; you can redistribute it and/or modify 
   6  * it under the terms of the GNU General Public License as published by 
   7  * the Free Software Foundation; either version 2 of the License, or (at
   8  * your option) any later version.
   9  * This program is distributed in the hope that it will be useful, but
  10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12  * more details.
  13  * 
  14  * You should have received a copy of the GNU General Public License along
  15  * with this program; if not, write to the Free Software Foundation, Inc.,
  16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17  *
  18  * Additional license information can be found in the documentation.
  19  * @author Erich P Gatejen
  20  */
  21 package autohit.vm;
  22 
  23 import autohit.common.ProcessMonitor;
  24 import autohit.server.SystemContext;
  25 import autohit.common.Constants;
  26 
  27 /**
  28  * A stand alone VM process context. It will wrap a VM in a thread and do all
  29  * the thread-safe kinda stuff. A context will handle successive VM runs, so
  30  * you can use them in a thread pool.
  31  * <p>
  32  * BE SURE to .init() and .start() this Thread BEFORE any other threads access
  33  * its methods (particularly execute()). Failure to heed this warning COULD
  34  * result in a race condition... :-)
  35  * <p>
  36  * 
  37  * <pre>
  38  *  There are four "commands" to a VM :
  39  *  	- PAUSE will hold execution
  40  *  	- RESUME will restart execution
  41  *  	- STOP will stop execution of the VM and dump it
  42  *  	- KILL will stop the execution and let this process die !
  43  * </pre>
  44  * 
  45  * @version 1.0 <i>Version History</i><code>EPG - Rewrite - 15May03<br>
  46  * EPG - moved context passing to the process, rather than the loader - 23Jul03</code>
  47  */
  48 public class VMProcessAutomat extends Thread implements VMProcess {
  49 
  50 	/**
  51 	 *  A runnable VM. This will be a fully implemented derived-class of VM. */
  52 	protected VM rVM;
  53 
  54 	/**
  55 	 *  An object for process management */
  56 	private ProcessMonitor vsBlock;
  57 
  58 	/**
  59 	 *  Running. The VMProcess is still valid */
  60 	private boolean alive;
  61 
  62 	/**
  63 	 *  Requests */
  64 	private boolean reqPause;
  65 	private boolean reqResume;
  66 	private boolean reqStop;
  67 	private boolean reqKill;
  68 	private boolean reqState;
  69 
  70 	/**
  71 	 *  My pid */
  72 	private int pid;
  73 
  74 	/**
  75 	 *  System Context */
  76 	private SystemContext sc;
  77 
  78 	/**
  79 	 *  Times run. This lets us know if we need to ma */
  80 	private int timesRun;
  81 
  82 	/**
  83 	 *  Constructor. */
  84 	public VMProcessAutomat() {
  85 
  86 		// be paranoid
  87 		rVM = null;
  88 		alive = true;
  89 		reqKill = false;
  90 		timesRun = 0;
  91 
  92 		this.setDaemon(true);
  93 
  94 		//Create the process monitor
  95 		vsBlock = new ProcessMonitor();
  96 	}
  97 
  98 	/**
  99 	 * Initialize the process controller. It takes an immutable SystemContext.
 100 	 * 
 101 	 * @param sctx
 102 	 *           a ready SystemContext. This will be fed to all controlled
 103 	 *           processes.
 104 	 * @param setpid
 105 	 *           the pid
 106 	 * @throws VMException
 107 	 *            is the process goes bad
 108 	 * @see autohit.vm.VM
 109 	 */
 110 	public void init(SystemContext sctx, int setpid) throws VMException {
 111 		sc = sctx;
 112 		pid = setpid;
 113 	}
 114 
 115 	/**
 116 	 * Load and Execute a VM. If a VM is already running, it will return false.
 117 	 * Otherwise, it will return true. Otherwise, it will load it and give the
 118 	 * greenlight to the VM thread.
 119 	 * <p>
 120 	 * This method can be called by any thread. It will clear the request
 121 	 * flags.
 122 	 * 
 123 	 * @param aVM
 124 	 *           A fully implimented derived-class of VM.
 125 	 * @return true if successful and execution begun, false if another VM is
 126 	 *         already running or there is an error.
 127 	 * @see autohit.vm.VM
 128 	 */
 129 	public boolean execute(VM aVM) {
 130 
 131 		if (rVM != null)
 132 			return false;
 133 		rVM = aVM;
 134 		alive = true;
 135 		reqKill = false;
 136 		reqPause = false;
 137 		reqResume = false;
 138 		reqStop = false;
 139 		reqState = false;
 140 
 141 		// Attach it
 142 		try {
 143 			aVM.attach(this);
 144 		} catch (Exception e) {
 145 			return false;
 146 		}
 147 
 148 		// Give the green light. NO NEW CODE AFTER THIS POINT!
 149 		// First make sure the run() is ready for us.
 150 		if (timesRun < 1) {
 151 			vsBlock.rendezous();
 152 		}
 153 		vsBlock.green();
 154 		return true;
 155 	}
 156 
 157 	/**
 158 	 * Pause the vm. This may be called by another thread. As with vmResume()
 159 	 * and vmStop(), it posts a request to the VMContext. The context will not
 160 	 * heed the request until the current VM.execute() is complete and the
 161 	 * context has a chance to check for these requests.
 162 	 * <p>
 163 	 * There is no rendezvous; the method will not block. All successive
 164 	 * requests count as the same request until the context services it. There
 165 	 * is no guarentee on the timing of the service. The Context will not check
 166 	 * for requests until AFTER the current instruction is complete. So, if the
 167 	 * vm is executing a long wait instruction, it could indeed be some time
 168 	 * before the request is serviced.
 169 	 * <p>
 170 	 * If you need to make sure that the request worked, then use the
 171 	 * verifyState() method to get the definative state of the VM/Context.
 172 	 * <p>
 173 	 * As for the return value, "success" merely means that the request was
 174 	 * successfully posted and not that the action was completed.
 175 	 * <p>
 176 	 * One last thaught: this is not a robust OS implimentation of a thread
 177 	 * context. You might want to restrict calls to vmPause() and vmResume() to
 178 	 * a single external thread. Multiple threads might get confused if they
 179 	 * don't cooperate...
 180 	 * 
 181 	 * @return true is successful. false if no vm is running or it is already
 182 	 *         paused.
 183 	 * @see autohit.vm.VM
 184 	 */
 185 	public boolean vmPause() {
 186 		if (rVM.getState() == VM.STATE_RUNNING) {
 187 			reqPause = true;
 188 			vsBlock.red();
 189 			return true;
 190 		} else
 191 			return false;
 192 	}
 193 
 194 	/**
 195 	 * Resume the vm.
 196 	 * <p>
 197 	 * See the notes for the pause() method.
 198 	 * 
 199 	 * @return true is successful. false if no vm is paused or it is already
 200 	 *         running.
 201 	 * @see autohit.vm.VM
 202 	 */
 203 	public boolean vmResume() {
 204 		if (rVM.getState() == VM.STATE_PAUSED) {
 205 			reqResume = true;
 206 			vsBlock.green();
 207 			return true;
 208 		} else
 209 			return false;
 210 	}
 211 
 212 	/**
 213 	 * Stop the vm. This will kill it permanently, so be careful.
 214 	 * <p>
 215 	 * See the notes for the pause() method.
 216 	 * 
 217 	 * @return true is successful. false if no vm is running or paused.
 218 	 * @see autohit.vm.VM
 219 	 */
 220 	public boolean vmStop() {
 221 		if (rVM == null)
 222 			return false;
 223 		else {
 224 			reqStop = true;
 225 			vsBlock.green();
 226 			return true;
 227 		}
 228 	}
 229 
 230 	/**
 231 	 * Get's the PID for this process
 232 	 * 
 233 	 * @return pid
 234 	 */
 235 	public int getPID() {
 236 		return pid;
 237 	}
 238 
 239 	/**
 240 	 * Join the process. ABSOLUTELY DO NOT CALL THIS FROM THE VMProcess thread.
 241 	 * You'll deadlock!
 242 	 */
 243 	public void joinIt() {
 244 		try {
 245 			this.join();
 246 		} catch (Exception ee) {
 247 			// Don't care
 248 		}
 249 	}
 250 
 251 	/**
 252 	 *  Kill this context.  It is irrevocable as it will interrupt the Thread.
 253 	 *  If you want to request the program to stop, call vmStop();
 254 	 * <p>
 255 	 * See the notes for the pause() method.
 256 	 * 
 257 	 * @return always returns true.
 258 	 * @see autohit.vm.VM
 259 	 */
 260 	public synchronized boolean kill() {
 261 		alive = false;
 262 		vsBlock.green();
 263 		interrupt();
 264 		return true;
 265 	}
 266 
 267 	/**
 268 	 * Verify the state of the VM. It will report a VM state value as defined
 269 	 * in the VM class--VM.State_*. This will be the authorative state, as this
 270 	 * method blocks until the VM has chance to clear requests and unblock it.
 271 	 * <p>
 272 	 * (And, never EVER call this method from within THIS thread. You'll almost
 273 	 * certainly deadlock it.)
 274 	 * <p>
 275 	 * The following describes each state:
 276 	 * 
 277 	 * <pre>
 278 	 *  STATE_NEW =
 279 	 *  	VM is loaded bu not started STATE_RUNNING =
 280 	 *  		VM is actively running.STATE_PAUSED = VM is paused.STATE_DONE = VM finished execution.This is rare,
 281 	 *  	as the VM will be automatically unloaded when finished.STATE_NO_VM = No VM is loaded into this context.
 282 	 *  </pre>
 283 	 * 
 284 	 * @return a VM.State_* value.
 285 	 * @see autohit.vm.VM
 286 	 */
 287 	public int verifyState() {
 288 		if (rVM == null)
 289 			return VM.STATE_NO_VM;
 290 		reqState = true;
 291 		vsBlock.rendezous();
 292 		return this.getState();
 293 	}
 294 
 295 	/**
 296 	 * A simple request for state. It may or not be stale be the time the
 297 	 * calling thread gets it. If you msut have THE AUTHORATIVE state, then
 298 	 * call verifyState()
 299 	 * 
 300 	 * @return a VM.State_* value.
 301 	 * @see autohit.vm.VM
 302 	 */
 303 	public int getState() {
 304 
 305 		if (rVM == null)
 306 			return VM.STATE_NO_VM;
 307 		else
 308 			return rVM.getState();
 309 	}
 310 
 311 	/**
 312 	 * Get my system context.
 313 	 * 
 314 	 * @return a SystemContext
 315 	 * @see autohit.vm.VM
 316 	 */
 317 	public SystemContext getSystemContext() {
 318 		return sc;
 319 	}
 320 
 321 	/**
 322 	 * Get a registered process attribute. Not implemented at this time.
 323 	 * 
 324 	 * @return an object that matches the name
 325 	 * @see autohit.vm.VM
 326 	 */
 327 	public Object processAttribute(String name) {
 328 		return null;
 329 	}
 330 
 331 	/**
 332 	 *  Run the context */
 333 	public void run() {
 334 
 335 		// Make this so that it that is is owned by this thread.
 336 		// and turn on the redlight. We won't do anything until someone
 337 		// else turns on the green.
 338 
 339 		// Outer loop controls the VMProcess
 340 		do {
 341 
 342 			// ---- DO NOT CHANGE ANYTHING BETWEEN HERE...
 343 			// Don't do anything until given the greenlight. However,
 344 			// if this is the first run, make sure the execute() does not
 345 			// complete before this thread makes it to here.
 346 			vsBlock.red();
 347 			if (timesRun < 1) {
 348 				vsBlock.rendezous();
 349 			}
 350 			vsBlock.stoplight();
 351 			timesRun++;
 352 			// --- ...AND HERE. ^^^^ NO CHANGE!!!!! ^^^^
 353 			// --- IF YOU DO, YOU'RE ASKING FOR a RACE CONDITION.
 354 
 355 			// catch any KILL request while there is no VM to run.
 356 			if (alive == false)
 357 				break;
 358 
 359 			// Inner loop controls a specific VM execution
 360 			// It's wrapped in a catch since any exception out of the VM
 361 			// is fatal.
 362 			try {
 363 
 364 				// Always run the first instruction with start
 365 				rVM.start();
 366 
 367 				do {
 368 
 369 					// DIE!
 370 					if (reqKill == true) {
 371 						alive = false;
 372 						break;
 373 					}
 374 
 375 					if (reqStop == true) {
 376 						reqStop = false;
 377 						rVM.die();
 378 						break; // bust out of the loop.
 379 					}
 380 
 381 					if (reqPause == true) {
 382 						rVM.pause();
 383 						reqPause = false;
 384 					}
 385 
 386 					if (reqResume == true) {
 387 						rVM.resume();
 388 						reqResume = false;
 389 					}
 390 
 391 					if (reqState == true) {
 392 						reqState = false;
 393 						vsBlock.rendezous();
 394 						// This might be dangerous, instead of a
 395 						// semiphore/signal. not sure
 396 						yield();
 397 						// We need to make sure the other thread has a chance
 398 						// to get the status.
 399 					}
 400 
 401 					// see if anyone is stopping us.
 402 					vsBlock.stoplight();
 403 
 404 					// Execute an instruciton. An exception
 405 					// kills this VM)
 406 					if (rVM.getState() == VM.STATE_RUNNING)
 407 						rVM.execute();
 408 
 409 				} while (alive);
 410 
 411 			} catch (VMException e) {
 412 
 413 				// PROCESS various codes.
 414 				if (e.numeric == VMException.CODE_SERVICE_INTENTIONAL_HALT) {
 415 					// ORDERED HALT
 416 					sc.getRootLogger().info("VMProcessAutomat: Process pid=" + pid + " ordered to stop.");
 417 					alive = false;
 418 
 419 				} else if (e.numeric == VMException.CODE_VM_DONE) {					
 420 			
 421 					sc.getRootLogger().info("VM: Program " + rVM.rootProgram + " done in pid=" + pid);
 422 					
 423 				} else if (e.numeric > VMException.FAULT) {
 424 					// FAULTED
 425 					try {
 426 						sc.getRootLogger().error(
 427 							"VMProcessAutomat: Process pid=" + pid + " died to FAULT.  message=" + e.getMessage(),
 428 							e.numeric);
 429 						rVM.myLog.error("VM: Process pid=" + pid + ".  I'm dying to a fatal fault.  message=" + e.getMessage(), e.numeric);
 430 					} catch (Exception epas) {
 431 					} // no chances
 432 					alive = false;
 433 				} else {
 434 					// ERROR. Don't die.
 435 					rVM.myLog.error(
 436 						"VM: Process pid=" + pid + " reported an exception.  Current program died.message=" + e.getMessage(),
 437 						e.numeric);
 438 				}
 439 
 440 			} catch (Exception e) {
 441 				sc.getRootLogger().error(
 442 					"VMProcessAutomat: Process pid="
 443 						+ pid
 444 						+ " died to unexpected exception.  The system may be unstable.  message="
 445 						+ e.getMessage(),
 446 					VMException.CODE_VM_PANIC);
 447 				rVM.myLog.error(
 448 					"VM: Process pid=" + pid + ".  I'm dying to a serious and unexpected exception.  message=" + e.getMessage(),
 449 					VMException.CODE_VM_PANIC);
 450 				alive = false;
 451 			}
 452 
 453 			// If we get here, the VM instance is dead
 454 			// force the finalization here.
 455 			try {
 456 				rVM.finalize();
 457 			} catch (Throwable ee) {
 458 				//dont care
 459 			}
 460 			rVM = null;
 461 
 462 		}
 463 		while (alive);
 464 
 465 		// If we get here, this process is dead.
 466 		// Last out the door turn off the lights... Make sure we haven't
 467 		// stopped anyone.
 468 		// DO NOT put ANY other code beyond the following statements
 469 		// or you are liable to deadlock other Threads.
 470 		vsBlock.green();
 471 		yield();
 472 	}
 473 
 474 	/**
 475 	 * Get's root program
 476 	 * 
 477 	 * @return string name of the root program
 478 	 */
 479 	public String getRootProgram() {
 480 		String result = Constants.UNKNOWN;
 481 		if (rVM != null) {
 482 			result = rVM.rootProgram;
 483 		}
 484 		return result;
 485 	}
 486 
 487 }