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 }