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 }
|