Skip to content

Commit 6cd1c7d

Browse files
Fix debug buttons disabled issue: send StoppeEvent after StepResponse/ContinuedResponse
Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>
1 parent 5e8c703 commit 6cd1c7d

2 files changed

Lines changed: 80 additions & 31 deletions

File tree

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/ProtocolServer.java

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
import java.io.OutputStream;
1616
import java.util.concurrent.CompletableFuture;
1717
import java.util.concurrent.CompletionException;
18+
import java.util.concurrent.ConcurrentLinkedQueue;
1819
import java.util.logging.Level;
1920
import java.util.logging.Logger;
2021

2122
import com.microsoft.java.debug.core.Configuration;
2223
import com.microsoft.java.debug.core.DebugException;
2324
import com.microsoft.java.debug.core.UsageDataSession;
2425
import com.microsoft.java.debug.core.protocol.AbstractProtocolServer;
26+
import com.microsoft.java.debug.core.protocol.Events.DebugEvent;
27+
import com.microsoft.java.debug.core.protocol.Events.StoppedEvent;
2528
import com.microsoft.java.debug.core.protocol.Messages;
2629
import com.sun.jdi.VMDisconnectedException;
2730

@@ -30,6 +33,8 @@ public class ProtocolServer extends AbstractProtocolServer {
3033

3134
private IDebugAdapter debugAdapter;
3235
private UsageDataSession usageDataSession = new UsageDataSession();
36+
private boolean isDispatchingRequest = false;
37+
private ConcurrentLinkedQueue<DebugEvent> eventQueue = new ConcurrentLinkedQueue<>();
3338

3439
/**
3540
* Constructs a protocol server instance based on the given input stream and output stream.
@@ -75,43 +80,87 @@ public CompletableFuture<Messages.Response> sendRequest(Messages.Request request
7580
}
7681

7782
@Override
78-
protected void dispatchRequest(Messages.Request request) {
79-
usageDataSession.recordRequest(request);
80-
debugAdapter.dispatchRequest(request).thenCompose((response) -> {
81-
CompletableFuture<Void> future = new CompletableFuture<>();
82-
if (response != null) {
83-
sendResponse(response);
84-
future.complete(null);
83+
public void sendEvent(DebugEvent event) {
84+
// See the two bugs https://github.com/Microsoft/java-debug/issues/134 and https://github.com/Microsoft/vscode/issues/58327,
85+
// it requires the java-debug to send the StoppedEvent after ContinueResponse/StepResponse is received by DA.
86+
if (event instanceof StoppedEvent) {
87+
sendEventLater(event);
88+
} else {
89+
super.sendEvent(event);
90+
}
91+
92+
}
93+
94+
/**
95+
* If the the dispatcher is idle, then send the event to the DA immediately.
96+
* Else add the new event to an eventQueue first and send them when dispatcher becomes idle again.
97+
* @param eventType
98+
* event type
99+
* @param body
100+
* event content
101+
*/
102+
private void sendEventLater(DebugEvent event) {
103+
synchronized (this) {
104+
if (this.isDispatchingRequest) {
105+
this.eventQueue.offer(event);
85106
} else {
86-
future.completeExceptionally(new DebugException("The request dispatcher should not return null response.",
87-
ErrorCode.UNKNOWN_FAILURE.getId()));
107+
super.sendEvent(event);
88108
}
89-
return future;
90-
}).exceptionally((ex) -> {
91-
Messages.Response response = new Messages.Response(request.seq, request.command);
92-
if (ex instanceof CompletionException && ex.getCause() != null) {
93-
ex = ex.getCause();
109+
}
110+
}
111+
112+
@Override
113+
protected void dispatchRequest(Messages.Request request) {
114+
usageDataSession.recordRequest(request);
115+
try {
116+
synchronized (this) {
117+
this.isDispatchingRequest = true;
94118
}
95119

96-
if (ex instanceof VMDisconnectedException) {
97-
// mark it success to avoid reporting error on VSCode.
98-
response.success = true;
99-
sendResponse(response);
100-
} else {
101-
String exceptionMessage = ex.getMessage() != null ? ex.getMessage() : ex.toString();
102-
ErrorCode errorCode = ex instanceof DebugException ? ErrorCode.parse(((DebugException) ex).getErrorCode()) : ErrorCode.UNKNOWN_FAILURE;
103-
boolean isUserError = ex instanceof DebugException && ((DebugException) ex).isUserError();
104-
if (isUserError) {
105-
usageDataSession.recordUserError(errorCode);
120+
debugAdapter.dispatchRequest(request).thenCompose((response) -> {
121+
CompletableFuture<Void> future = new CompletableFuture<>();
122+
if (response != null) {
123+
sendResponse(response);
124+
future.complete(null);
106125
} else {
107-
logger.log(Level.SEVERE, String.format("[error response][%s]: %s", request.command, exceptionMessage), ex);
126+
future.completeExceptionally(new DebugException("The request dispatcher should not return null response.",
127+
ErrorCode.UNKNOWN_FAILURE.getId()));
108128
}
129+
return future;
130+
}).exceptionally((ex) -> {
131+
Messages.Response response = new Messages.Response(request.seq, request.command);
132+
if (ex instanceof CompletionException && ex.getCause() != null) {
133+
ex = ex.getCause();
134+
}
135+
136+
if (ex instanceof VMDisconnectedException) {
137+
// mark it success to avoid reporting error on VSCode.
138+
response.success = true;
139+
sendResponse(response);
140+
} else {
141+
String exceptionMessage = ex.getMessage() != null ? ex.getMessage() : ex.toString();
142+
ErrorCode errorCode = ex instanceof DebugException ? ErrorCode.parse(((DebugException) ex).getErrorCode()) : ErrorCode.UNKNOWN_FAILURE;
143+
boolean isUserError = ex instanceof DebugException && ((DebugException) ex).isUserError();
144+
if (isUserError) {
145+
usageDataSession.recordUserError(errorCode);
146+
} else {
147+
logger.log(Level.SEVERE, String.format("[error response][%s]: %s", request.command, exceptionMessage), ex);
148+
}
149+
150+
sendResponse(AdapterUtils.setErrorResponse(response,
151+
errorCode,
152+
exceptionMessage));
153+
}
154+
return null;
155+
}).join();
156+
} finally {
157+
synchronized (this) {
158+
this.isDispatchingRequest = false;
159+
}
109160

110-
sendResponse(AdapterUtils.setErrorResponse(response,
111-
errorCode,
112-
exceptionMessage));
161+
while (this.eventQueue.peek() != null) {
162+
super.sendEvent(this.eventQueue.poll());
113163
}
114-
return null;
115-
}).join();
164+
}
116165
}
117166
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/RestartFrameHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ private void stepInto(IDebugAdapterContext context, ThreadReference thread) {
106106
context.getDebugSession().getEventHub().stepEvents().filter(debugEvent -> request.equals(debugEvent.event.request())).take(1).subscribe(debugEvent -> {
107107
debugEvent.shouldResume = false;
108108
// Have to send two events to keep the UI sync with the step in operations:
109-
context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID()));
110109
context.getProtocolServer().sendEvent(new Events.ContinuedEvent(thread.uniqueID()));
110+
context.getProtocolServer().sendEvent(new Events.StoppedEvent("restartframe", thread.uniqueID()));
111111
});
112112
request.enable();
113113
thread.resume();

0 commit comments

Comments
 (0)