From 45c70c29e572de3e9ca24d993f0616fd31f05029 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 4 Sep 2020 01:19:38 +0200 Subject: [PATCH] Fixes #83672 --- src/vs/base/parts/ipc/common/ipc.net.ts | 73 ++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index c3fede90bf31e..bc8e97c269cce 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -533,6 +533,53 @@ class Queue { } } +class LoadEstimator { + + private static _HISTORY_LENGTH = 10; + private static _INSTANCE: LoadEstimator | null = null; + public static getInstance(): LoadEstimator { + if (!LoadEstimator._INSTANCE) { + LoadEstimator._INSTANCE = new LoadEstimator(); + } + return LoadEstimator._INSTANCE; + } + + private lastRuns: number[]; + + constructor() { + this.lastRuns = []; + const now = Date.now(); + for (let i = 0; i < LoadEstimator._HISTORY_LENGTH; i++) { + this.lastRuns[i] = now - 1000 * i; + } + setInterval(() => { + for (let i = LoadEstimator._HISTORY_LENGTH; i >= 1; i--) { + this.lastRuns[i] = this.lastRuns[i - 1]; + } + this.lastRuns[0] = Date.now(); + }, 1000); + } + + /** + * returns an estimative number, from 0 (low load) to 1 (high load) + */ + public load(): number { + const now = Date.now(); + const historyLimit = (1 + LoadEstimator._HISTORY_LENGTH) * 1000; + let score = 0; + for (let i = 0; i < LoadEstimator._HISTORY_LENGTH; i++) { + if (now - this.lastRuns[i] <= historyLimit) { + score++; + } + } + return 1 - score / LoadEstimator._HISTORY_LENGTH; + } + + public hasHighLoad(): boolean { + return this.load() >= 0.5; + } +} + /** * Same as Protocol, but will actually track messages and acks. * Moreover, it will ensure no messages are lost if there are no event listeners. @@ -559,6 +606,8 @@ export class PersistentProtocol implements IMessagePassingProtocol { private _socketReader: ProtocolReader; private _socketDisposables: IDisposable[]; + private readonly _loadEstimator = LoadEstimator.getInstance(); + private readonly _onControlMessage = new BufferedEmitter(); readonly onControlMessage: Event = this._onControlMessage.event; @@ -670,15 +719,19 @@ export class PersistentProtocol implements IMessagePassingProtocol { const timeSinceLastIncomingMsg = Date.now() - this._socketReader.lastReadTime; if (timeSinceLastIncomingMsg >= ProtocolConstants.KeepAliveTimeoutTime) { - // Trash the socket - this._onSocketTimeout.fire(undefined); - return; + // It's been a long time since we received a server message + // But this might be caused by the event loop being busy and failing to read messages + if (!this._loadEstimator.hasHighLoad()) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } } this._incomingKeepAliveTimeout = setTimeout(() => { this._incomingKeepAliveTimeout = null; this._recvKeepAliveCheck(); - }, ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg + 5); + }, Math.max(ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg, 0) + 5); } public getSocket(): ISocket { @@ -821,15 +874,19 @@ export class PersistentProtocol implements IMessagePassingProtocol { const oldestUnacknowledgedMsg = this._outgoingUnackMsg.peek()!; const timeSinceOldestUnacknowledgedMsg = Date.now() - oldestUnacknowledgedMsg.writtenTime; if (timeSinceOldestUnacknowledgedMsg >= ProtocolConstants.AcknowledgeTimeoutTime) { - // Trash the socket - this._onSocketTimeout.fire(undefined); - return; + // It's been a long time since our sent message was acknowledged + // But this might be caused by the event loop being busy and failing to read messages + if (!this._loadEstimator.hasHighLoad()) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } } this._outgoingAckTimeout = setTimeout(() => { this._outgoingAckTimeout = null; this._recvAckCheck(); - }, ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg + 5); + }, Math.max(ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg, 0) + 5); } private _sendAck(): void {