未验证 提交 b041f460 编写于 作者: A Alex Dima

Show a disconnection dialog only after 40-50 seconds

上级 455b029a
......@@ -747,6 +747,10 @@ export class PersistentProtocol implements IMessagePassingProtocol {
return this._socket;
}
public getMillisSinceLastIncomingData(): number {
return Date.now() - this._socketReader.lastReadTime;
}
public beginAcceptReconnection(socket: ISocket, initialDataChunk: VSBuffer | null): void {
this._isReconnecting = true;
......
......@@ -333,11 +333,15 @@ export const enum PersistentConnectionEventType {
}
export class ConnectionLostEvent {
public readonly type = PersistentConnectionEventType.ConnectionLost;
constructor(
public readonly millisSinceLastIncomingData: number
) { }
}
export class ReconnectionWaitEvent {
public readonly type = PersistentConnectionEventType.ReconnectionWait;
constructor(
public readonly durationSeconds: number,
public readonly millisSinceLastIncomingData: number,
private readonly cancellableTimer: CancelablePromise<void>
) { }
......@@ -347,6 +351,9 @@ export class ReconnectionWaitEvent {
}
export class ReconnectionRunningEvent {
public readonly type = PersistentConnectionEventType.ReconnectionRunning;
constructor(
public readonly millisSinceLastIncomingData: number
) { }
}
export class ConnectionGainEvent {
public readonly type = PersistentConnectionEventType.ConnectionGain;
......@@ -413,7 +420,7 @@ abstract class PersistentConnection extends Disposable {
}
const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true);
this._options.logService.info(`${logPrefix} starting reconnecting loop. You can get more information with the trace log level.`);
this._onDidStateChange.fire(new ConnectionLostEvent());
this._onDidStateChange.fire(new ConnectionLostEvent(this.protocol.getMillisSinceLastIncomingData()));
const TIMES = [5, 5, 10, 10, 10, 10, 10, 30];
const disconnectStartTime = Date.now();
let attempt = -1;
......@@ -422,7 +429,7 @@ abstract class PersistentConnection extends Disposable {
const waitTime = (attempt < TIMES.length ? TIMES[attempt] : TIMES[TIMES.length - 1]);
try {
const sleepPromise = sleep(waitTime);
this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime, sleepPromise));
this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime, this.protocol.getMillisSinceLastIncomingData(), sleepPromise));
this._options.logService.info(`${logPrefix} waiting for ${waitTime} seconds before reconnecting...`);
try {
......@@ -435,7 +442,7 @@ abstract class PersistentConnection extends Disposable {
}
// connection was lost, let's try to re-establish it
this._onDidStateChange.fire(new ReconnectionRunningEvent());
this._onDidStateChange.fire(new ReconnectionRunningEvent(this.protocol.getMillisSinceLastIncomingData()));
this._options.logService.info(`${logPrefix} resolving connection...`);
const simpleOptions = await resolveConnectionOptions(this._options, this.reconnectionToken, this.protocol);
this._options.logService.info(`${logPrefix} connecting to ${simpleOptions.host}:${simpleOptions.port}...`);
......
......@@ -37,7 +37,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { ReconnectionWaitEvent, PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
import Severity from 'vs/base/common/severity';
import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { SwitchRemoteViewItem, SwitchRemoteAction } from 'vs/workbench/contrib/remote/browser/explorerViewItems';
import { Action, IActionViewItem } from 'vs/base/common/actions';
......@@ -53,7 +53,6 @@ import { Event } from 'vs/base/common/event';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator';
import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess';
import * as icons from 'vs/workbench/contrib/remote/browser/remoteIcons';
......@@ -598,6 +597,7 @@ Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions
class VisibleProgress {
public readonly location: ProgressLocation;
private _isDisposed: boolean;
private _lastReport: string | null;
private _currentProgressPromiseResolve: (() => void) | null;
......@@ -609,6 +609,7 @@ class VisibleProgress {
}
constructor(progressService: IProgressService, location: ProgressLocation, initialReport: string | null, buttons: string[], onDidCancel: (choice: number | undefined, lastReport: string | null) => void) {
this.location = location;
this._isDisposed = false;
this._lastReport = initialReport;
this._currentProgressPromiseResolve = null;
......@@ -694,28 +695,39 @@ class ReconnectionTimer2 implements IDisposable {
}
}
class RemoteAgentConnectionStatusListener implements IWorkbenchContribution {
/**
* The time when a prompt is shown to the user
*/
const DISCONNECT_PROMPT_TIME = 40 * 1000; // 40 seconds
class RemoteAgentConnectionStatusListener extends Disposable implements IWorkbenchContribution {
constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
@IProgressService progressService: IProgressService,
@IDialogService dialogService: IDialogService,
@ICommandService commandService: ICommandService,
@IContextKeyService contextKeyService: IContextKeyService
@IQuickInputService quickInputService: IQuickInputService
) {
super();
const connection = remoteAgentService.getConnection();
if (connection) {
let quickInputVisible = false;
quickInputService.onShow(() => quickInputVisible = true);
quickInputService.onHide(() => quickInputVisible = false);
let visibleProgress: VisibleProgress | null = null;
let lastLocation: ProgressLocation.Dialog | ProgressLocation.Notification | null = null;
let reconnectWaitEvent: ReconnectionWaitEvent | null = null;
let disposableListener: IDisposable | null = null;
function showProgress(location: ProgressLocation.Dialog | ProgressLocation.Notification, buttons: { label: string, callback: () => void }[], initialReport: string | null = null): VisibleProgress {
function showProgress(location: ProgressLocation.Dialog | ProgressLocation.Notification | null, buttons: { label: string, callback: () => void }[], initialReport: string | null = null): VisibleProgress {
if (visibleProgress) {
visibleProgress.dispose();
visibleProgress = null;
}
lastLocation = location;
if (!location) {
location = quickInputVisible ? ProgressLocation.Notification : ProgressLocation.Dialog;
}
return new VisibleProgress(
progressService, location, initialReport, buttons.map(button => button.label),
......@@ -757,6 +769,12 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution {
}
};
// Possible state transitions:
// ConnectionGain -> ConnectionLost
// ConnectionLost -> ReconnectionWait
// ReconnectionWait -> ReconnectionRunning
// ReconnectionRunning -> ConnectionGain, ReconnectionPermanentFailure
connection.onDidStateChange((e) => {
if (visibleProgress) {
visibleProgress.stopTimer();
......@@ -768,30 +786,33 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution {
}
switch (e.type) {
case PersistentConnectionEventType.ConnectionLost:
if (!visibleProgress) {
visibleProgress = showProgress(ProgressLocation.Dialog, [reconnectButton, reloadButton]);
if (visibleProgress || e.millisSinceLastIncomingData > DISCONNECT_PROMPT_TIME) {
if (!visibleProgress) {
visibleProgress = showProgress(null, [reconnectButton, reloadButton]);
}
visibleProgress.report(nls.localize('connectionLost', "Connection Lost"));
}
visibleProgress.report(nls.localize('connectionLost', "Connection Lost"));
break;
case PersistentConnectionEventType.ReconnectionWait:
reconnectWaitEvent = e;
visibleProgress = showProgress(lastLocation || ProgressLocation.Notification, [reconnectButton, reloadButton]);
visibleProgress.startTimer(Date.now() + 1000 * e.durationSeconds);
if (visibleProgress || e.millisSinceLastIncomingData > DISCONNECT_PROMPT_TIME) {
reconnectWaitEvent = e;
visibleProgress = showProgress(null, [reconnectButton, reloadButton]);
visibleProgress.startTimer(Date.now() + 1000 * e.durationSeconds);
}
break;
case PersistentConnectionEventType.ReconnectionRunning:
visibleProgress = showProgress(lastLocation || ProgressLocation.Notification, [reloadButton]);
visibleProgress.report(nls.localize('reconnectionRunning', "Attempting to reconnect..."));
if (visibleProgress || e.millisSinceLastIncomingData > DISCONNECT_PROMPT_TIME) {
visibleProgress = showProgress(null, [reloadButton]);
visibleProgress.report(nls.localize('reconnectionRunning', "Attempting to reconnect..."));
// Register to listen for quick input is opened
disposableListener = contextKeyService.onDidChangeContext((contextKeyChangeEvent) => {
const reconnectInteraction = new Set<string>([inQuickPickContextKeyValue]);
if (contextKeyChangeEvent.affectsSome(reconnectInteraction)) {
// Register to listen for quick input is opened
disposableListener = quickInputService.onShow(() => {
// Need to move from dialog if being shown and user needs to type in a prompt
if (lastLocation === ProgressLocation.Dialog && visibleProgress !== null) {
if (visibleProgress && visibleProgress.location === ProgressLocation.Dialog) {
visibleProgress = showProgress(ProgressLocation.Notification, [reloadButton], visibleProgress.lastReport);
}
}
});
});
}
break;
case PersistentConnectionEventType.ReconnectionPermanentFailure:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册