Skip to content

Commit 2106dbb

Browse files
Fix debug buttons disabled issue: send StoppedEvent after StepResponse/ContinueResponse (#227)
* Fix debug buttons disabled issue: send StoppeEvent after StepResponse/ContinuedResponse Signed-off-by: Jinbo Wang <[email protected]> * Remove the incorrect comments * Use private object as lock
1 parent 5e8c703 commit 2106dbb

File tree

2 files changed

+78
-31
lines changed

2 files changed

+78
-31
lines changed

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

Lines changed: 77 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

@@ -31,6 +34,10 @@ public class ProtocolServer extends AbstractProtocolServer {
3134
private IDebugAdapter debugAdapter;
3235
private UsageDataSession usageDataSession = new UsageDataSession();
3336

37+
private Object lock = new Object();
38+
private boolean isDispatchingRequest = false;
39+
private ConcurrentLinkedQueue<DebugEvent> eventQueue = new ConcurrentLinkedQueue<>();
40+
3441
/**
3542
* Constructs a protocol server instance based on the given input stream and output stream.
3643
* @param input
@@ -75,43 +82,83 @@ public CompletableFuture<Messages.Response> sendRequest(Messages.Request request
7582
}
7683

7784
@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);
85+
public void sendEvent(DebugEvent event) {
86+
// See the two bugs https://github.com/Microsoft/java-debug/issues/134 and https://github.com/Microsoft/vscode/issues/58327,
87+
// it requires the java-debug to send the StoppedEvent after ContinueResponse/StepResponse is received by DA.
88+
if (event instanceof StoppedEvent) {
89+
sendEventLater(event);
90+
} else {
91+
super.sendEvent(event);
92+
}
93+
94+
}
95+
96+
/**
97+
* If the the dispatcher is idle, then send the event to the DA immediately.
98+
* Else add the new event to an eventQueue first and send them when dispatcher becomes idle again.
99+
*/
100+
private void sendEventLater(DebugEvent event) {
101+
synchronized (lock) {
102+
if (this.isDispatchingRequest) {
103+
this.eventQueue.offer(event);
85104
} else {
86-
future.completeExceptionally(new DebugException("The request dispatcher should not return null response.",
87-
ErrorCode.UNKNOWN_FAILURE.getId()));
105+
super.sendEvent(event);
88106
}
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();
107+
}
108+
}
109+
110+
@Override
111+
protected void dispatchRequest(Messages.Request request) {
112+
usageDataSession.recordRequest(request);
113+
try {
114+
synchronized (lock) {
115+
this.isDispatchingRequest = true;
94116
}
95117

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

110-
sendResponse(AdapterUtils.setErrorResponse(response,
111-
errorCode,
112-
exceptionMessage));
159+
while (this.eventQueue.peek() != null) {
160+
super.sendEvent(this.eventQueue.poll());
113161
}
114-
return null;
115-
}).join();
162+
}
116163
}
117164
}

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)