1    	package acl2s.uilib.session;
2    	
3    	import static acl2s.uilib.session.ISessionStatusListener.BUSY_SESSION;
4    	import static acl2s.uilib.session.ISessionStatusListener.ERROR_SESSION;
5    	import static acl2s.uilib.session.ISessionStatusListener.NO_SESSION;
6    	import static acl2s.uilib.session.ISessionStatusListener.READY_CMD_SESSION;
7    	import static acl2s.uilib.session.ISessionStatusListener.READY_MISC_SESSION;
8    	import static acl2s.uilib.session.ISessionStatusListener.STARTING_SESSION;
9    	
10   	import java.io.File;
11   	import java.io.IOException;
12   	import java.util.HashSet;
13   	import java.util.List;
14   	
15   	import org.peterd.util.BoundedBlockingQueue;
16   	import org.peterd.util.Pingable;
17   	
18   	import acl2s.lib.parse.CanonicalParseContext;
19   	import acl2s.lib.parse.ILispUnparsable;
20   	import acl2s.lib.parse.IParseContext;
21   	import acl2s.lib.parse.ParseException;
22   	import acl2s.lib.parse.StringParser;
23   	import acl2s.lib.parse.obj.LispObject;
24   	import acl2s.lib.session.IEnvInfo;
25   	import acl2s.lib.session.IResultInfo;
26   	import acl2s.lib.session.IStatusInfo;
27   	import acl2s.lib.session.InteractiveSession;
28   	import acl2s.lib.session.MyInteractiveSessionConfig;
29   	import acl2s.lib.session.SessionBusyException;
30   	import acl2s.lib.session.SessionDeadException;
31   	import acl2s.lib.session.SessionNotBusyException;
32   	import acl2s.lib.session.SessionStateException;
33   	import acl2s.uilib_driver.Acl2sTargetedOperationException;
34   	import acl2s.uilib_driver.IAcl2sGraphics;
35   	import acl2s.uilib_driver.IAcl2sGraphicsFactory;
36   	import acl2s.uilib_driver.IAcl2sUiLibDriver;
37   	import acl2s.uilib_driver.IAcl2sUiLibErrorHandler;
38   	import acl2s.uilib_driver.IAcl2sUiLibThreadManager;
39   	
40   	public class InteractiveSessionManager implements Pingable {
41   		static final boolean SNIP_STARTUP = true;  // for debugging
42   		
43   		protected final IAcl2sUiLibDriver driver;
44   		protected final IAcl2sUiLibThreadManager threadmgr;
45   		protected final IAcl2sUiLibErrorHandler errorHandler;
46   		protected final IAcl2sGraphicsFactory graphicsFactory;
47   		protected final ISessionClient client;
48   		protected final HashSet<ISessionStatusListener> listeners = new HashSet<ISessionStatusListener>(); 
49   		protected ISessionModel model;
50   		protected InteractiveSession session = null;
51   		protected boolean sessionReady = false;
52   		protected SessionThread thread = null;
53   		
54   		protected int status = 0;
55   		protected Exception exception = null; // non-null iff status is error
56   			
57   		public InteractiveSessionManager(IAcl2sUiLibDriver d, ISessionClient c, ISessionModel m) {
58   			client = c;
59   			model = m;
60   			driver = d;
61   			threadmgr = d.getThreadManager();
62   			errorHandler = d.getErrorHandler();
63   			graphicsFactory = d.getGraphicsFactory();
64   		}
65   		
66   	
67   		public synchronized int getStatus() {
68   			return status;
69   		}
70   	
71   		public boolean isAlive() {
72   			return getStatus() != NO_SESSION;
73   		}
74   	
75   		public synchronized Exception getError() {
76   			if (status == ERROR_SESSION) {
77   				assert exception != null;
78   				return exception;
79   			} else {
80   				assert exception == null;
81   				return null;
82   			}
83   		}
84   		
85   		public long getSecret() {
86   			if (session != null)
87   				return session.getSecret();
88   			else
89   				return 0;
90   		}
91   		
92   		
93   		public synchronized IEnvInfo getCurrentEnv() throws SessionBusyException, SessionDeadException {
94   			if (session != null) {
95   				IEnvInfo e = session.getEnvInfo();
96   				if (e == null) {
97   					throw new SessionBusyException();
98   				} else {
99   					return e;
100  				}
101  			} else {
102  				throw new SessionDeadException();
103  			}
104  		}
105  	
106  		public synchronized void updateModelEnv() throws SessionBusyException, SessionDeadException {
107  			if (session != null) {
108  				IEnvInfo e = session.getEnvInfo();
109  				if (e == null) {
110  					throw new SessionBusyException();
111  				} else {
112  					model.updateLastEnv(e);
113  				}
114  			} else {
115  				throw new SessionDeadException();
116  			}
117  		}
118  		
119  		/* Internal stuff called from either thread. */
120  		protected void updateStatus() {
121  			boolean asyncUpdate = false;
122  			synchronized (this) {
123  				int x;
124  					if (session == null) {
125  						x = NO_SESSION;
126  					} else if (!sessionReady) {
127  						x = STARTING_SESSION;
128  					} else if (thread.waiting) {
129  						if (thread.cmdInput) {
130  							x = READY_CMD_SESSION;
131  						} else {
132  							x = READY_MISC_SESSION;
133  						}
134  					} else {
135  						x = BUSY_SESSION;
136  					}
137  				if (x != status) {
138  					status = x;
139  					if (threadmgr.inEventThread()) {
140  						for (ISessionStatusListener l : listeners) {
141  							l.sessionStatusChanged(status);
142  						}
143  					} else {
144  						asyncUpdate = true;
145  					}
146  				}
147  			}
148  			if (asyncUpdate) {
149  				threadmgr.asyncExec(new Runnable() {
150  					public void run() {
151  						synchronized (InteractiveSessionManager.this) {
152  							for (ISessionStatusListener l : listeners) {
153  								l.sessionStatusChanged(status);
154  							}
155  						}
156  					}
157  				});
158  			}
159  		}
160  	
161  		
162  		
163  		
164  		/* These are always called from event thread. */
165  		public void addSessionStatusListener(ISessionStatusListener l) {
166  			listeners.add(l);
167  			l.sessionStatusChanged(getStatus());
168  		}
169  		
170  		public void removeSessionStatusListener(ISessionStatusListener l) {
171  			listeners.remove(l);
172  		}
173  		
174  		public synchronized void startNewSession(ISessionModel toModel, MyInteractiveSessionConfig config) throws IllegalStateException {
175  			assert threadmgr.inEventThread();
176  			if (session != null) {
177  				if (session.isAlive()) {
178  					throw new IllegalStateException();
179  				}
180  			}
181  			sessionReady = false;
182  			File dir = client.getDesiredCBD(); //safe since already in event thread
183  			session = new InteractiveSession(driver,config,dir);
184  			model = toModel;
185  			model.newSession();
186  			thread = new SessionThread(session,toModel);
187  			updateStatus();
188  			thread.start();
189  		}	
190  		
191  		public synchronized void endSession() throws SessionStateException, IllegalStateException {
192  			if (session == null) throw new IllegalStateException();
193  			if (!thread.waiting) throw new SessionBusyException();
194  			thread.noMoreInput();
195  			thread = null;
196  			session.close();
197  			session = null;
198  			updateStatus();
199  		}
200  		
201  		public synchronized void killSession() {
202  			if (session == null) return;
203  			thread.noMoreInput();
204  			thread = null;
205  			session.forkKill();
206  			session = null;
207  			updateStatus();
208  		}
209  	
210  		public synchronized void interruptSession() throws SessionDeadException {
211  			try {
212  				if (session == null) throw new SessionDeadException();
213  				session.interrupt();
214  				ping();
215  			} catch (SessionNotBusyException e) {
216  				// ignore
217  			} finally {
218  				updateStatus();
219  			}
220  		}
221  	
222  		public synchronized void scheduleAbort() throws SessionDeadException {
223  			if (thread == null) throw new SessionDeadException();
Event field_access: Accessing field "acl2s.uilib.session.InteractiveSessionManager$SessionThread.doAbort".
224  			thread.doAbort = true;
225  			
226  			ping();
227  		}
228  	
229  		public synchronized void dispose() {
230  			if (thread != null) {
231  				thread.noMoreInput();
232  				thread = null;
233  			}
234  			if (session != null) {
235  				session.dispose();
236  				session = null;
237  			}
238  			model = null;
239  		}	
240  		
241  		/* null means i won't be able to answer that question without a restart.
242  		 * SessionStateException means you caught me at the wrong time.  */
243  		public IResultInfo systemCmd(String cmd) throws SessionStateException {
244  			synchronized (this) {
245  				if (session == null) return null;
246  				try {
247  					IResultInfo r;
248  					do r = session.systemCmd(cmd);
249  					while (r.wasInterrupted());
250  					return r;
251  				} catch (SessionDeadException e) {
252  					killSession();
253  					return null;
254  				}
255  			}
256  		}
257  		
258  		public IResultInfo systemCmd2(String cmd) throws SessionStateException, SessionDeadException {
259  			IResultInfo foo = systemCmd(cmd);
260  			if (foo == null) {
261  				throw new SessionDeadException();
262  			} else {
263  				return foo;
264  			}
265  		}
266  		
267  		
268  		protected class SessionThread extends Thread {
269  			InteractiveSession ses;
270  			ISessionModel modl;
271  			StringBuffer snippedStartup;
272  			boolean flushed;
273  			boolean acl2WasStarted = false;
274  			IAcl2sGraphics graphics = null;
275  			
276  			public SessionThread(InteractiveSession s, ISessionModel to) {
277  				ses = s;
278  				modl = to;
279  			}
280  	
281  			public void run() {
282  				boolean startupProblem = false;
283  				try {
284  					threadmgr.addShutdownHook(ses.getKiller());
285  					ses.beginInit();
286  					threadmgr.syncExec(new Runnable() {
287  						public void run() {
288  							modl.appendOutputLine("Executing " + ses.getCmdLine()[0] + '\n');
289  							modl.appendOutputLine("Starting ACL2 in mode \"" + ses.getConfig().getDescriptionName() + "\"\n");
290  						}});
291  					snippedStartup = new StringBuffer();
292  					doStartupOutputAndPrompt();
293  					if (ses.getStatusInfo().wasInterrupted()) {
294  						throw new SessionDeadException();
295  					}
296  					synchronized (InteractiveSessionManager.this) {
297  						if (session == ses) {
298  							sessionReady = true;
299  						}
300  					}
301  					snippedStartup = null;
302  					
303  					updateStatus();
304  	
305  					// after startup
306  					for (;;) {
307  						synchronized (InteractiveSessionManager.this) { if (session != ses) break; }
308  						
309  						getNextInput();
310  						
311  						synchronized (InteractiveSessionManager.this) { if (session != ses) break; }
312  						
313  						if (in instanceof IInputInfo) { // cmd input
314  							IInputInfo f = (IInputInfo) in;
315  							nextInput(f);
316  							ses.userCmd(f.getACL2Input());
317  						} else if (in instanceof String) { // misc input
318  							break; // shouldn't happen here
319  						} else { // quit 
320  							if (quit) {
321  								ses.kill();
322  								break;
323  							}
324  							// else, interrupted => more output
325  						}
326  						
327  						doOutputAndPrompt();
328  					}
329  				} catch (SessionStateException sbe) {
330  					errorHandler.error("Error in ACL2 session thread.", sbe);
331  				} catch (IOException e) {
332  					startupProblem = true;
333  					errorHandler.warning("Error while creating ACL2 session.", e);
334  				} catch (SessionDeadException e) {
335  					String msg = "ACL2 died unexpectedly.";
336  					synchronized (InteractiveSessionManager.this) {
337  						if (session != ses) return;
338  						if (!sessionReady) {
339  							msg += " This could be caused by an incompatible/non-existant ACL2 version.";
340  							startupProblem = true;
341  						} else if (ses.getStatusInfo().wasInterrupted()) {
342  							if (File.separatorChar == '\\') {
343  								msg += " This could be caused by this build of ACL2" +
344  								"\nnot properly handling interruption requests (which has been fixed in" +
345  								"\nacl2_image 3.3.0.1; check for ACL2s updates), or this could just be the" +
346  								"\nunreliability of ACL2 in Windows.  Fortunately, \"Restart session\" should" +
347  								"\nautomatically reprocess everything to return you to the state you were in." +
348  								"";
349  							}
350  						}
351  					}
352  					errorHandler.warning(msg, e);
353  					modlOutput.enqueue("\n\n");
354  					modlOutput.enqueue(msg);
355  				} finally {
356  					
357  					synchronized (InteractiveSessionManager.this) {
358  						if (session == ses) {
359  							session = null;
360  						}
361  						if (thread == Thread.currentThread()) {
362  							thread = null;
363  						}
364  						updateStatus();
365  					}
366  					ses.kill();
367  					threadmgr.removeShutdownHook(ses.getKiller());
368  					if (startupProblem) {
369  						if (snippedStartup != null) {
370  							modlOutput.enqueue("\n========== previously hidden output (for debugging purposes): ==========\n");
371  							modlOutput.enqueue(snippedStartup.toString());
372  							threadmgr.asyncExec(modlOutputUpdaterStarter);
373  						}
374  						if (!acl2WasStarted) {
375  							errorHandler.needsImage(ses.getConfig());
376  						} else if (snippedStartup != null) {
377  							if (snippedStartup.indexOf("\"Recertify ACL2s system books\"") >= 0) {
378  								errorHandler.needsHooksCertification(ses.getConfig());
379  							} else if (snippedStartup.indexOf("This ACL2 image is older") >= 0) {
380  								errorHandler.needsImage(ses.getConfig());
381  							}
382  						}
383  					}
384  				}
385  			}
386  	
387  			private void nextInput(final IInputInfo i) {
388  				threadmgr.syncExec(new Runnable() {
389  					public void run() {
390  						if (quit) return;
391  						modl.nextInput(i);
392  					}});
393  			}
394  			
395  			private void doStartupOutputAndPrompt() throws SessionStateException, SessionDeadException {
396  				flushed = false;
397  				doStartupOutputUntilPrompt();
398  				if (ses.getEnvInfo() == null) {
399  					//ses.kill(); // will happen
400  					throw new SessionDeadException();
401  				}
402  				if (!modlOutput.isEmpty()) {
403  					threadmgr.syncExec(modlOutputSyncUpdater);
404  				}
405  				doPrompt();
406  			}
407  	
408  			private void doOutputAndPrompt() throws SessionStateException, SessionDeadException {
409  				flushed = false;
410  				doOutputUntilPrompt();
411  				if (!modlOutput.isEmpty()) {
412  					threadmgr.syncExec(modlOutputSyncUpdater);
413  				}
414  				doPrompt();
415  			}
416  	
417  			private void doPrompt() throws SessionStateException, SessionDeadException {
418  				final IStatusInfo info = ses.getStatusInfo();
419  				final IEnvInfo env = ses.getEnvInfo();
420  				if (!quit) {
421  					threadmgr.syncExec(new Runnable() {
422  						public void run() {
423  							int idx = modl.finishOutput(info,env);
424  							if (!env.inWormhole()) {
425  								int i = idx;
426  								while (i > 0 && modl.getEnv(i).inWormhole()) i--;
427  								client.commandCompleted(modl.getInput(i),info,i);
428  							}
429  						}});
430  				}
431  			}
432  			
433  			private void doStartupOutputUntilPrompt() throws SessionDeadException, SessionStateException {
434  				String line;
435  				for (;;) {
436  					line = ses.readLineUntilPrompt();
437  					if (line == null) {
438  						return;
439  					} else {
440  						asyncModlAppend(line);
441  						if (line.indexOf("ACL2 Version ") >= 0) {
442  							break;
443  						}
444  					}
445  				}
446  				for (;;) {
447  					line = ses.readLineUntilPrompt();
448  					if (line == null) {
449  						return;
450  					} else {
451  						asyncModlAppend(line);
452  						if (line.indexOf("General Public License") >= 0) {
453  							break;
454  						}
455  					}
456  				}
457  				acl2WasStarted = true;
458  				asyncModlAppend("\n For ACL2 (theorem prover) help, refer to\n   " +
459  						ses.getConfig().getBaseConfig().getDocBaseURL() + "acl2-doc.html\n" +
460  						" For ACL2s (interface) help, refer to\n   " +
461  						ses.getConfig().getBaseConfig().getACL2sDocURL() + "\n" +
462  						driver.getExtraInstructionOutput() +
463  						"\n");
464  				for (;;) {
465  					if (SNIP_STARTUP) {
466  						for (;;) {
467  							line = ses.readLineUntilPrompt();
468  							if (line == null) {
469  								return;
470  							} else {
471  								if (snippedStartup != null) {
472  									snippedStartup.append(line);
473  								}
474  								if (line.endsWith("${NoMoReSnIp}$\n")) {
475  									break;
476  								}
477  							}
478  						}
479  					}
480  					for (;;) {
481  						line = ses.readLineUntilPrompt();
482  						if (line == null) {
483  							return;
484  						} else {
485  							if (SNIP_STARTUP && line.endsWith("${SnIpMeHeRe}$\n")) {
486  								if (snippedStartup != null) {
487  									snippedStartup.append(line);
488  								}
489  								break;
490  							}
491  							asyncModlAppend(line);
492  						}
493  					}
494  				}
495  			}
496  			
497  			private transient StringBuffer graphicsStuff = new StringBuffer();
498  			
499  			private void openGraphics() {
500  				if (graphics == null && graphicsFactory != null) {
501  					graphics = graphicsFactory.createGraphics(new Pingable[] { InteractiveSessionManager.this },
502  							                                  new File[] { client.getDesiredCBD() });
503  				}
504  			}
505  			
506  			private void disposeGraphics() {
507  				if (graphics != null) {
508  					final IAcl2sGraphics oldGraphics = graphics;
509  					graphics = null;
510  					threadmgr.asyncExec(new Runnable() {
511  						public void run() {
512  							oldGraphics.dispose();
513  						}					
514  					});
515  				}
516  			}
517  			
518  			// non-null => incomplete stuff (null line)
519  			private String doGraphicsOp() throws SessionDeadException, SessionStateException {
520  				graphicsStuff.setLength(0);
521  				for (;;) {
522  					String line = ses.readLineUntilPrompt();
523  					if (line == null) {
524  						// must have been interrupted
525  						// hmm.
526  						return graphicsStuff.toString();
527  					} else {
528  						if (line.endsWith(END_GRAPHICS_OP)) {
529  							int len = line.length();
530  							line = line.substring(0, len - END_GRAPHICS_OP_LEN);
531  							graphicsStuff.append(line);
532  							break;
533  						} else {
534  							graphicsStuff.append(line);
535  						}
536  					}
537  				}
538  				openGraphics();
539  				if (graphics != null) {
540  					try {
541  						final LispObject[] ops = LispObject.parseAll(new StringParser(CanonicalParseContext.instance, graphicsStuff));
542  						threadmgr.asyncExec(new Runnable() {
543  							public void run() {
544  								for (LispObject op : ops) {
545  									try {
546  										graphics.processTargetedOperation(op);
547  									} catch (Acl2sTargetedOperationException e) {
548  										IEnvInfo env = modl.getEnv(modl.countCompletedCmds());
549  										IParseContext pc;
550  										if (env != null) {
551  											pc = env.getParseContext();
552  										} else {
553  											pc = null;
554  										}
555  										modl.appendOutputLine("ACL2s Error: While processing graphics operation,\n\t" +
556  												op.toString(pc) + "\nthe following error was reported:\n\t" + e.toString() + "\n");
557  									}
558  								}
559  							}
560  						});
561  					} catch (ParseException e) {
562  						errorHandler.error("Bad graphics output read (ignored)", e);
563  					}
564  				}
565  				return null;
566  			}
567  			
568  			// non-null => incomplete stuff (null line)
569  			private String doGraphicsEvent() throws SessionDeadException, SessionStateException {
570  				graphicsStuff.setLength(0);
571  				openGraphics();
572  				for (;;) {
573  					String line = ses.readLineUntilPrompt();
574  					if (line == null) {
575  						// Read-object
576  						
577  						if (ses.interrupted() || ses.getEnvInfo() != null) {
578  							return graphicsStuff.toString();
579  						}
580  						
581  						if (graphics != null) {
582  							getNextGraphicsEvent();
583  							if (in == null) {
584  								if (ses.interrupted() || ses.getEnvInfo() != null) {
585  									return graphicsStuff.toString();
586  								}
587  								ses.miscInput("()");
588  							} else {
589  								ses.miscInput(((ILispUnparsable)in).toString(null));
590  							}
591  						} else {
592  							ses.miscInput("()"); // send nil if no graphics
593  						}
594  						
595  					} else if (line.endsWith(END_GRAPHICS_EVENT)) {
596  						return null;
597  					} else {
598  						graphicsStuff.append(line);
599  					}
600  				}
601  			}
602  			
603  			private void doOutputUntilPrompt() throws SessionDeadException, SessionStateException {
604  				try {
605  					for (;;) {
606  						String line = ses.readLineUntilPrompt();
607  						if (line == null) {
608  							if (checkPrompt()) {
609  								return;
610  							}
611  						} else {
612  							if (line.endsWith("$\n")) {
613  								if (line.endsWith(BEGIN_GRAPHICS_OP)) {
614  									int len = line.length();
615  									line = line.substring(0, len - BEGIN_GRAPHICS_OP_LEN);
616  									String leftovers = doGraphicsOp();
617  									if (leftovers != null) {
618  										line = line.concat(leftovers);
619  									}
620  								} else if (line.endsWith(BEGIN_GRAPHICS_EVENT)) {
621  									int len = line.length();
622  									line = line.substring(0, len - BEGIN_GRAPHICS_EVENT_LEN);
623  									String leftovers = doGraphicsEvent();
624  									if (leftovers != null) {
625  										line = line.concat(leftovers);
626  									}
627  								}
628  							}
629  							if (line.length() > 0) {
630  								asyncModlAppend(line);
631  							}
632  						}
633  					}
634  				} finally {
635  					disposeGraphics();
636  				}
637  			}
638  			
639  			private boolean checkPrompt() throws SessionDeadException, SessionStateException {
640  				if (ses.getEnvInfo() == null) {
641  					getNextInput();
642  					if (in instanceof IInputInfo) { // cmd input
643  						return true; // shouldn't happen here
644  					} else if (in instanceof String) { // misc input
645  						// TODO: special support for misc input?
646  						final String m = (String) in;
647  						ses.miscInput(m);
648  						threadmgr.syncExec(new Runnable() {
649  							public void run() {
650  								if (quit) return;
651  								modl.appendOutputLine(m + '\n');
652  							}});
653  						return false;
654  					} else { 
655  						if (quit) {
656  							ses.kill();
657  							return true;
658  						} else {
659  							//interrupted => more output
660  							return false;
661  						}
662  					}
663  				} else {
664  					return true;
665  				}
666  			}
667  			
668  			private void asyncModlAppend(final String s) {
669  				if (modlOutput.maybeEnqueue(s)) {
670  					// enqueued
671  					if (modlOutput.size() == 1) {
672  						// was empty & now not empty
673  						threadmgr.asyncExec(modlOutputUpdaterStarter);
674  					}
675  				} else {
676  					threadmgr.syncExec(new Runnable() {
677  						public void run() {
678  							List<String> ss = modlOutput.maybeDequeueAll();
679  							if (ss.size() > 0) {
680  								modl.appendOutputLines(ss);
681  								//System.out.println("Flush!");
682  							}
683  							modlOutput.enqueue(s);
684  							flushed = true;
685  						}
686  					});
687  				}
688  			}
689  			
690  			final BoundedBlockingQueue<String> modlOutput = new BoundedBlockingQueue<String>(100);
691  			final Runnable modlOutputUpdaterStarter = new Runnable() {
692  				public void run() {
693  					if (quit) return;
694  					threadmgr.delayedExec(modlOutputAsyncUpdater, 150, true);
695  				}
696  			};
697  	
698  			final Runnable modlOutputAsyncUpdater = new Runnable() {
699  				public void run() {
700  					if (quit) return;
701  					if (flushed) {
702  						flushed = false;
703  						threadmgr.delayedExec(modlOutputAsyncUpdater, 150, true);
704  					} else {
705  						List<String> ss = modlOutput.maybeDequeueAll();
706  						if (ss.size() > 0) {
707  							modl.appendOutputLines(ss);
708  							//System.out.println("Update!");
709  						}
710  					}
711  				}
712  			};
713  	
714  			final Runnable modlOutputSyncUpdater = new Runnable() {
715  				public void run() {
716  					List<String> ss = modlOutput.maybeDequeueAll();
717  					if (ss.size() > 0) {
718  						modl.appendOutputLines(ss);
719  						//System.out.println("SyncUpdate!");
720  					}
721  				}
722  			};
723  	
724  			
725  			/* stuff that communicates with session manager */
726  			boolean cmdInput = false;
727  			boolean waiting = false;
728  			
729  			Object in = null;
730  			
731  			boolean quit = false;
732  			boolean doAbort = false;
733  			
734  			void getNextInput() throws SessionDeadException {
Event lock_event: Locking "acl2s.uilib.session.InteractiveSessionManager$SessionThread.this$0".
Event lock_event: Locking "acl2s.uilib.session.InteractiveSessionManager$SessionThread.this$0".
Also see events: [unguarded_access][guarded_by_access][guarded_by_access]
735  				synchronized (InteractiveSessionManager.this) {
736  					if (waiting) throw new IllegalStateException("Multiple threads trying to get session input!");
737  					in = null;
738  					if (!ses.isAlive()) throw new SessionDeadException();
739  					if (quit) return;
740  					waiting = true;
741  					IEnvInfo env = ses.getEnvInfo();
742  					cmdInput = env != null;
743  					for (;;) {
744  						if (!ses.isAlive()) throw new SessionDeadException();
745  						if (env == null && ses.interrupted()) break;
Event guarded_by_access: Field "acl2s.uilib.session.InteractiveSessionManager$SessionThread.doAbort" guarded by lock "acl2s.uilib.session.InteractiveSessionManager$SessionThread.this$0".
Also see events: [unguarded_access][lock_event][lock_event][guarded_by_access]
746  						if (doAbort && env != null) { 
Event guarded_by_access: Field "acl2s.uilib.session.InteractiveSessionManager$SessionThread.doAbort" guarded by lock "acl2s.uilib.session.InteractiveSessionManager$SessionThread.this$0".
Also see events: [unguarded_access][lock_event][guarded_by_access][lock_event]
747  							doAbort = false;
748  							if (env.getLdLevel() > 1) {
749  								in = AbortInput.instance;
750  								break;
751  							}
752  						}
753  						final boolean clientRead = env != null && !env.inWormhole();
754  						threadmgr.asyncExec(new Runnable() {
755  							public void run() {
756  								synchronized (InteractiveSessionManager.this) {
757  									if (clientRead) {
758  										if (in != null) { InteractiveSessionManager.this.notifyAll(); return; }
759  										in = client.nextInputOption();
760  										if (in != null) { InteractiveSessionManager.this.notifyAll(); return; }
761  									}
762  									updateStatus();
763  								}
764  							}
765  						});
766  						//if (in != null) break;
767  						try {
768  							InteractiveSessionManager.this.wait();
769  						} catch (InterruptedException e) { }
770  						if (in != null) break;
771  						if (quit) { in = null; break; }
772  					}
773  					waiting = false;
774  					updateStatus();
775  				}
776  			}
777  			
778  			void getNextGraphicsEvent() throws SessionDeadException {
779  				synchronized (InteractiveSessionManager.this) {
780  					if (waiting) throw new IllegalStateException("Multiple threads trying to get session input!");
781  					in = null;
782  					waiting = true;
783  					for (;;) {
784  						if (quit || ses.getEnvInfo() != null) break;
785  						if (!ses.isAlive()) throw new SessionDeadException();
786  						if (ses.interrupted()) break;
787  						
788  						in = graphics.maybePopNextEvent();
789  						
790  						if (in != null) break;
791  						try {
792  							InteractiveSessionManager.this.wait();
793  						} catch (InterruptedException e) { }
794  					}
795  					waiting = false;
796  					updateStatus();
797  				}
798  			}
799  			
800  			// these are called already synchronized on InteractiveSessionManager.this
801  			void noMoreInput() {
802  				quit = true;
803  				InteractiveSessionManager.this.notifyAll();
804  			}
805  			
806  			void ping() {
807  				InteractiveSessionManager.this.notifyAll();
808  			}
809  			
810  			void submitCmdInput(IInputInfo cmd) throws SessionStateException, SessionDeadException {
811  				if (!waiting) throw new SessionBusyException();
812  				if (quit) throw new SessionDeadException();
813  				if (in != null) throw new SessionBusyException();
814  				if (!cmdInput) throw new SessionStateException();
815  				in = cmd;
816  				InteractiveSessionManager.this.notifyAll();
817  			}
818  			
819  			void submitMiscInput(String misc) throws SessionStateException, SessionDeadException {
820  				if (!waiting) throw new SessionBusyException();
821  				if (quit) throw new SessionDeadException();
822  				if (in != null) throw new SessionBusyException();
823  				if (cmdInput) throw new SessionStateException();
824  				in = misc;
825  				InteractiveSessionManager.this.notifyAll();
826  			}
827  		}
828  		
829  		
830  		synchronized SessionThread getThread() {
831  			return thread;
832  		}
833  			
834  		public synchronized void ping() {
835  			if (thread != null) {
836  				thread.ping();
837  			} 
838  			/* else {  // simulate UNDO and REDO even with no session
839  				IInputInfo in = client.nextInputOption();
840  				if (in instanceof UndoInput) { // undo or redo
841  					final UndoInput undo = (UndoInput) in;
842  					
843  					IEnvInfo env1 = model.getEnv(model.countCompletedCmds());
844  					int superLen = env1.getSuperDoneSize() + env1.getSuperUndoneSize();
845  					IEnvInfo env2 = undo.getUndoneTargetEnv(superLen);
846  					
847  					try {
848  						model.nextInput(undo);
849  						final int idx = model.finishOutput(undo, env2);
850  						client.commandCompleted(undo,undo,idx);
851  					} catch (IllegalStateException ise) {
852  						// ignore
853  					}
854  				}
855  			} */
856  		}
857  	
858  		public synchronized void submitCmdInput(IInputInfo cmdIn) throws SessionStateException, SessionDeadException {
859  			try {
860  				if (thread != null) {
861  					thread.submitCmdInput(cmdIn);
862  				} else {
863  					throw new SessionDeadException();
864  				}
865  			} finally {
866  				updateStatus();
867  			}
868  		}
869  		
870  		public synchronized void submitMiscInput(String miscIn) throws SessionStateException, SessionDeadException {
871  			try {
872  				if (thread != null) {
873  					thread.submitMiscInput(miscIn);
874  				} else {
875  					throw new SessionDeadException();
876  				}
877  			} finally {
878  				updateStatus();
879  			}
880  		}
881  		
882  		public synchronized void noMoreInput() {
883  			if (thread != null) {
884  				thread.noMoreInput();
885  			}
886  		}
887  	
888  	
889  	/*
890  		public Runnable blockingInputSubmitter(final IInputInfo input) {
891  			return new Runnable() {
892  				public void run() {
893  					synchronized (A2sSessionManager.this) {
894  						while (status == BUSY_SESSION && thread != null && !thread.waiting) {
895  							try {
896  								A2sSessionManager.this.wait();
897  							} catch (InterruptedException e) {
898  							}
899  						}
900  						if (status == READY_SESSION) {
901  							try {
902  								submitMiscInput(input);
903  							} catch (SessionBusyException e) {
904  								// impossible
905  							} catch (SessionDeadException e) {
906  								// impossible
907  							}
908  						}
909  					}
910  				}
911  			};
912  		}
913  	*/
914  	
915  		static final String BEGIN_GRAPHICS_OP = "$GrApHiCs-Op{$\n";
916  		static final int BEGIN_GRAPHICS_OP_LEN = BEGIN_GRAPHICS_OP.length();
917  		static final String END_GRAPHICS_OP = "$GrApHiCs-Op}$\n";
918  		static final int END_GRAPHICS_OP_LEN = END_GRAPHICS_OP.length();
919  		static final String BEGIN_GRAPHICS_EVENT = "$GrApHiCs-EvEnT{$\n";
920  		static final int BEGIN_GRAPHICS_EVENT_LEN = BEGIN_GRAPHICS_EVENT.length();
921  		static final String END_GRAPHICS_EVENT = "$GrApHiCs-EvEnT}$\n";
922  		static final int END_GRAPHICS_EVENT_LEN = END_GRAPHICS_EVENT.length();
923  		
924  	}