Skip to content

Commit 7fcd81c

Browse files
committed
Fixes microsoft#58794: Use acknowledge messages to determine extension host responsiveness
1 parent 5996144 commit 7fcd81c

1 file changed

Lines changed: 34 additions & 20 deletions

File tree

src/vs/workbench/services/extensions/node/rpcProtocol.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
123123
private readonly _cancelInvokedHandlers: { [req: string]: () => void; };
124124
private readonly _pendingRPCReplies: { [msgId: string]: LazyPromise; };
125125
private _responsiveState: ResponsiveState;
126-
private _potentialUnresponsiveRequests: number[];
126+
private _unacknowledgedCount: number;
127127
private _unresponsiveTime: number;
128128
private _asyncCheckUresponsive: RunOnceScheduler;
129129

@@ -143,7 +143,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
143143
this._cancelInvokedHandlers = Object.create(null);
144144
this._pendingRPCReplies = {};
145145
this._responsiveState = ResponsiveState.Responsive;
146-
this._potentialUnresponsiveRequests = [];
146+
this._unacknowledgedCount = 0;
147147
this._unresponsiveTime = 0;
148148
this._asyncCheckUresponsive = this._register(new RunOnceScheduler(() => this._checkUnresponsive(), 1000));
149149
this._protocol.onMessage((msg) => this._receiveOneMessage(msg));
@@ -160,25 +160,22 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
160160
}
161161

162162
private _onWillSendRequest(req: number): void {
163-
if (this._potentialUnresponsiveRequests.length === 0) {
163+
if (this._unacknowledgedCount === 0) {
164164
// Since this is the first request we are sending in a while,
165165
// mark this moment as the start for the countdown to unresponsive time
166166
this._unresponsiveTime = Date.now() + RPCProtocol.UNRESPONSIVE_TIME;
167167
}
168-
this._potentialUnresponsiveRequests.push(req);
168+
this._unacknowledgedCount++;
169169
if (!this._asyncCheckUresponsive.isScheduled()) {
170170
this._asyncCheckUresponsive.schedule();
171171
}
172172
}
173173

174-
private _onWillReceiveReply(req: number): void {
174+
private _onDidReceiveAcknowledge(req: number): void {
175175
// The next possible unresponsive time is now + delta.
176176
this._unresponsiveTime = Date.now() + RPCProtocol.UNRESPONSIVE_TIME;
177-
// Remove all previous requests from the potential unresponsive list
178-
while (this._potentialUnresponsiveRequests.length > 0 && this._potentialUnresponsiveRequests[0] <= req) {
179-
this._potentialUnresponsiveRequests.shift();
180-
}
181-
if (this._potentialUnresponsiveRequests.length === 0) {
177+
this._unacknowledgedCount--;
178+
if (this._unacknowledgedCount === 0) {
182179
// No more need to check for unresponsive
183180
this._asyncCheckUresponsive.cancel();
184181
}
@@ -187,12 +184,12 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
187184
}
188185

189186
private _checkUnresponsive(): void {
190-
if (this._potentialUnresponsiveRequests.length === 0) {
187+
if (this._unacknowledgedCount === 0) {
191188
// Not waiting for anything => cannot say if it is responsive or not
192189
return;
193190
}
194191

195-
if (this._potentialUnresponsiveRequests.length >= 2 && Date.now() > this._unresponsiveTime) {
192+
if (Date.now() > this._unresponsiveTime) {
196193
// Unresponsive!!
197194
this._setResponsiveState(ResponsiveState.Unresponsive);
198195
} else {
@@ -286,6 +283,13 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
286283
this._receiveRequest(msgLength, req, rpcId, method, args, (messageType === MessageType.RequestMixedArgsWithCancellation));
287284
break;
288285
}
286+
case MessageType.Acknowledged: {
287+
if (this._logger) {
288+
this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`);
289+
}
290+
this._onDidReceiveAcknowledge(req);
291+
break;
292+
}
289293
case MessageType.Cancel: {
290294
this._receiveCancel(msgLength, req);
291295
break;
@@ -343,6 +347,13 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
343347

344348
this._cancelInvokedHandlers[callId] = cancel;
345349

350+
// Acknowledge the request
351+
const msg = MessageIO.serializeAcknowledged(req);
352+
if (this._logger) {
353+
this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`);
354+
}
355+
this._protocol.send(msg);
356+
346357
promise.then((r) => {
347358
delete this._cancelInvokedHandlers[callId];
348359
if (this._uriTransformer) {
@@ -384,7 +395,6 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
384395

385396
const pendingReply = this._pendingRPCReplies[callId];
386397
delete this._pendingRPCReplies[callId];
387-
this._onWillReceiveReply(req);
388398

389399
pendingReply.resolveOk(value);
390400
}
@@ -401,7 +411,6 @@ export class RPCProtocol extends Disposable implements IRPCProtocol {
401411

402412
const pendingReply = this._pendingRPCReplies[callId];
403413
delete this._pendingRPCReplies[callId];
404-
this._onWillReceiveReply(req);
405414

406415
let err: Error = null;
407416
if (value && value.$isError) {
@@ -705,6 +714,10 @@ class MessageIO {
705714
};
706715
}
707716

717+
public static serializeAcknowledged(req: number): Buffer {
718+
return MessageBuffer.alloc(MessageType.Acknowledged, req, 0).buffer;
719+
}
720+
708721
public static serializeCancel(req: number): Buffer {
709722
return MessageBuffer.alloc(MessageType.Cancel, req, 0).buffer;
710723
}
@@ -788,12 +801,13 @@ const enum MessageType {
788801
RequestJSONArgsWithCancellation = 2,
789802
RequestMixedArgs = 3,
790803
RequestMixedArgsWithCancellation = 4,
791-
Cancel = 5,
792-
ReplyOKEmpty = 6,
793-
ReplyOKBuffer = 7,
794-
ReplyOKJSON = 8,
795-
ReplyErrError = 9,
796-
ReplyErrEmpty = 10,
804+
Acknowledged = 5,
805+
Cancel = 6,
806+
ReplyOKEmpty = 7,
807+
ReplyOKBuffer = 8,
808+
ReplyOKJSON = 9,
809+
ReplyErrError = 10,
810+
ReplyErrEmpty = 11,
797811
}
798812

799813
const enum ArgType {

0 commit comments

Comments
 (0)