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