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


Reply via email to