Skip to content

Commit 4c2f775

Browse files
committed
Improve connection logic (fixes microsoft/vscode-remote-release#1224)
1 parent f58c8f6 commit 4c2f775

11 files changed

Lines changed: 273 additions & 134 deletions

File tree

src/vs/base/parts/ipc/common/ipc.net.ts

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -405,42 +405,53 @@ export class Client<TContext = string> extends IPCClient<TContext> {
405405
/**
406406
* Will ensure no messages are lost if there are no event listeners.
407407
*/
408-
export function createBufferedEvent<T>(source: Event<T>): Event<T> {
409-
let emitter: Emitter<T>;
410-
let hasListeners = false;
411-
let isDeliveringMessages = false;
412-
let bufferedMessages: T[] = [];
413-
414-
const deliverMessages = () => {
415-
if (isDeliveringMessages) {
408+
export class BufferedEmitter<T> {
409+
private _emitter: Emitter<T>;
410+
public readonly event: Event<T>;
411+
412+
private _hasListeners = false;
413+
private _isDeliveringMessages = false;
414+
private _bufferedMessages: T[] = [];
415+
416+
constructor() {
417+
this._emitter = new Emitter<T>({
418+
onFirstListenerAdd: () => {
419+
this._hasListeners = true;
420+
// it is important to deliver these messages after this call, but before
421+
// other messages have a chance to be received (to guarantee in order delivery)
422+
// that's why we're using here nextTick and not other types of timeouts
423+
process.nextTick(() => this._deliverMessages);
424+
},
425+
onLastListenerRemove: () => {
426+
this._hasListeners = false;
427+
}
428+
});
429+
430+
this.event = this._emitter.event;
431+
}
432+
433+
private _deliverMessages(): void {
434+
if (this._isDeliveringMessages) {
416435
return;
417436
}
418-
isDeliveringMessages = true;
419-
while (hasListeners && bufferedMessages.length > 0) {
420-
emitter.fire(bufferedMessages.shift()!);
437+
this._isDeliveringMessages = true;
438+
while (this._hasListeners && this._bufferedMessages.length > 0) {
439+
this._emitter.fire(this._bufferedMessages.shift()!);
421440
}
422-
isDeliveringMessages = false;
423-
};
441+
this._isDeliveringMessages = false;
442+
}
424443

425-
source((e: T) => {
426-
bufferedMessages.push(e);
427-
deliverMessages();
428-
});
429-
430-
emitter = new Emitter<T>({
431-
onFirstListenerAdd: () => {
432-
hasListeners = true;
433-
// it is important to deliver these messages after this call, but before
434-
// other messages have a chance to be received (to guarantee in order delivery)
435-
// that's why we're using here nextTick and not other types of timeouts
436-
process.nextTick(deliverMessages);
437-
},
438-
onLastListenerRemove: () => {
439-
hasListeners = false;
440-
}
441-
});
442-
443-
return emitter.event;
444+
public fire(event: T): void {
445+
if (this._hasListeners) {
446+
this._emitter.fire(event);
447+
} else {
448+
this._bufferedMessages.push(event);
449+
}
450+
}
451+
452+
public flushBuffer(): void {
453+
this._bufferedMessages = [];
454+
}
444455
}
445456

446457
class QueueElement<T> {
@@ -530,20 +541,20 @@ export class PersistentProtocol implements IMessagePassingProtocol {
530541
private _socketReader: ProtocolReader;
531542
private _socketDisposables: IDisposable[];
532543

533-
private _onControlMessage = new Emitter<VSBuffer>();
534-
readonly onControlMessage: Event<VSBuffer> = createBufferedEvent(this._onControlMessage.event);
544+
private readonly _onControlMessage = new BufferedEmitter<VSBuffer>();
545+
readonly onControlMessage: Event<VSBuffer> = this._onControlMessage.event;
535546

536-
private _onMessage = new Emitter<VSBuffer>();
537-
readonly onMessage: Event<VSBuffer> = createBufferedEvent(this._onMessage.event);
547+
private readonly _onMessage = new BufferedEmitter<VSBuffer>();
548+
readonly onMessage: Event<VSBuffer> = this._onMessage.event;
538549

539-
private _onClose = new Emitter<void>();
540-
readonly onClose: Event<void> = createBufferedEvent(this._onClose.event);
550+
private readonly _onClose = new BufferedEmitter<void>();
551+
readonly onClose: Event<void> = this._onClose.event;
541552

542-
private _onSocketClose = new Emitter<void>();
543-
readonly onSocketClose: Event<void> = createBufferedEvent(this._onSocketClose.event);
553+
private readonly _onSocketClose = new BufferedEmitter<void>();
554+
readonly onSocketClose: Event<void> = this._onSocketClose.event;
544555

545-
private _onSocketTimeout = new Emitter<void>();
546-
readonly onSocketTimeout: Event<void> = createBufferedEvent(this._onSocketTimeout.event);
556+
private readonly _onSocketTimeout = new BufferedEmitter<void>();
557+
readonly onSocketTimeout: Event<void> = this._onSocketTimeout.event;
547558

548559
public get unacknowledgedCount(): number {
549560
return this._outgoingMsgId - this._outgoingAckId;
@@ -656,6 +667,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
656667
this._isReconnecting = true;
657668

658669
this._socketDisposables = dispose(this._socketDisposables);
670+
this._onControlMessage.flushBuffer();
671+
this._onSocketClose.flushBuffer();
672+
this._onSocketTimeout.flushBuffer();
673+
this._socket.dispose();
659674

660675
this._socket = socket;
661676
this._socketWriter = new ProtocolWriter(this._socket);

0 commit comments

Comments
 (0)