Hi Gary,
I'm not sure how buffering helps. When is the buffered output
printed? It can't be deferred until the event handler, because the
command is async and the prompt is suppose to be printed out right
away, not deferred until the event comes in. w.r.t. this
particular issue, isn't the prompt the only thing that
executeCommand() is printing? How would buffering help in that
case.
I understand your concerns about using a lock, but I still think
it's probably the way to go. We can possibly work around the two
pitfalls you point out.
The call to executeCommand() from the event handler due to monitor
commands should not be an issue since it is on a separate thread.
In fact synchronization should end up working just the way we
want. The monitor commands will be block from execution until the
executeCommand() from the command thread has finished printing the
prompt. And if the monitor command that is executed has async
completion of its own, the event handler won't see it until it's
done with all monitor commands it's currently executing from
vmInterrupted().
I'm not to sure what the recursive use of executeCommand() is. Can
you point it out to me?
thanks,
Chris
On 6/7/18 5:48 AM, Gary Adams wrote:
It's theoretically possible that the corrupted output
could be produced with a single step command
because of the separate event handler and command
processing threads, although the failures that have
been recorded were all observed later in a test scenario.
So let's look at a third possible way to fix the issue.
- we need a mechanism that will protect executeCommand and
handleEvent
- to ensure that neither produces a partial output
A few key snippets from the code below.
The method executeCommand() is called from both
the command processing thread and the event handler
thread. It is also called recursively, so a lock may not be
appropriate
The eventHandler output is produce incrementally
- handleEvent produces the header, e.g. "Set completed:"
- vmInterrupted() produces
printCurrentLocation()
monitor commands output (if any) by calling
executeCommand()
printPrompt()
I don't have concrete fix to propose, yet, but I'm leaning towards
having executeCommand buffer it's output and return it as a single
string. It would involve a getPrompt() instead of printPrompt, so
the text could be composed. It would also require aggregating any
output that is currently sent directly to MessageOutput.
It might also be addressed by enhancing MessageOutput to
include some sort of buffering for the current event or current
command.
It's unlikely this would be ready before the jdk11 repos are
cloned, so
it may be best to target these bugs for the next release.
src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/TTY.java:
...
209 public void vmInterrupted() {
210 Thread.yield(); // fetch output
211 printCurrentLocation();
212 for (String cmd : monitorCommands) {
213 StringTokenizer t = new
StringTokenizer(cmd);
214 t.nextToken(); // get rid of monitor
number
215 executeCommand(t);
216 }
217 MessageOutput.printPrompt();
218 }
...
391 void executeCommand(StringTokenizer t) {
392 String cmd = t.nextToken().toLowerCase();
393 // Normally, prompt for the next command after
this one is done
394 boolean showPrompt = true;
395
...
589 if (showPrompt) {
590 MessageOutput.printPrompt();
591 }
592 }
src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/EventHandler.java:
...
66 public void run() {
67 EventQueue queue = Env.vm().eventQueue();
68 while (connected) {
69 try {
70 EventSet eventSet = queue.remove();
71 boolean resumeStoppedApp = false;
72 EventIterator it =
eventSet.eventIterator();
73 while (it.hasNext()) {
74 resumeStoppedApp |=
!handleEvent(it.nextEvent());
75 }
76
77 if (resumeStoppedApp) {
78 eventSet.resume();
79 } else if (eventSet.suspendPolicy() ==
EventRequest.SUSPEND_ALL) {
80 setCurrentThread(eventSet);
81 notifier.vmInterrupted();
82 }
83 } catch (InterruptedException exc) {
84 // Do nothing. Any changes will be
seen at top of loop.
85 } catch (VMDisconnectedException discExc)
{
86 handleDisconnectedException();
87 break;
88 }
89 }
90 synchronized (this) {
91 completed = true;
92 notifyAll();
93 }
94 }
96 private boolean handleEvent(Event event) {
97 notifier.receivedEvent(event);
98
99 if (event instanceof ExceptionEvent) {
100 return exceptionEvent(event);
101 } else if (event instanceof BreakpointEvent) {
102 return breakpointEvent(event);
103 } else if (event instanceof WatchpointEvent) {
104 return fieldWatchEvent(event);
105 } else if (event instanceof StepEvent) {
106 return stepEvent(event);
107 } else if (event instanceof MethodEntryEvent)
{
108 return methodEntryEvent(event);
109 } else if (event instanceof MethodExitEvent) {
110 return methodExitEvent(event);
111 } else if (event instanceof ClassPrepareEvent)
{
112 return classPrepareEvent(event);
113 } else if (event instanceof ClassUnloadEvent)
{
114 return classUnloadEvent(event);
115 } else if (event instanceof ThreadStartEvent)
{
116 return threadStartEvent(event);
117 } else if (event instanceof ThreadDeathEvent)
{
118 return threadDeathEvent(event);
119 } else if (event instanceof VMStartEvent) {
120 return vmStartEvent(event);
121 } else {
122 return handleExitEvent(event);
123 }
124 }
On 6/6/18, 3:27 PM, Chris Plummer wrote:
...
The point I'm trying to get across is I don't think the issue is
with the rate of commands being sent. I think the issue can be
isolated to a single step command that has the first prompt
appearing in the middle of the "Step completed: ..." output.
command thread: receive and execute "step" command
event thread: receive "step" breakpoint. print "Step completed:
"
command thread: print "main[1]"
event thread: print text that comes after "Step completed: "
event thread: print "main[1]"
This has to be fixed in the sender of the output, not the
receiver of the output (or the sender of the command).
Chris
...
|