Skip to content

Commit b2c06a5

Browse files
committed
Address multiple-init scenario
1 parent a97a334 commit b2c06a5

File tree

1 file changed

+46
-31
lines changed

1 file changed

+46
-31
lines changed

Extension/src/LanguageServer/client.ts

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export function hasTrustedCompilerPaths(): boolean {
8383

8484
// Data shared by all clients.
8585
let languageClient: LanguageClient;
86-
let firstClientStarted: Promise<void>;
86+
let firstClientStarted: Promise<boolean>;
8787
let languageClientCrashedNeedsRestart: boolean = false;
8888
const languageClientCrashTimes: number[] = [];
8989
let compilerDefaults: configs.CompilerDefaults | undefined;
@@ -1191,34 +1191,36 @@ export class DefaultClient implements Client {
11911191
const rootUri: vscode.Uri | undefined = this.RootUri;
11921192
this.settingsTracker = new SettingsTracker(rootUri);
11931193

1194-
try {
1195-
let isFirstClient: boolean = false;
1196-
if (!languageClient || languageClientCrashedNeedsRestart) {
1197-
if (languageClientCrashedNeedsRestart) {
1198-
languageClientCrashedNeedsRestart = false;
1199-
// if we're recovering, the isStarted needs to be reset.
1200-
// because we're starting the first client again.
1201-
DefaultClient.isStarted.reset();
1202-
}
1203-
firstClientStarted = this.createLanguageClient();
1204-
util.setProgress(util.getProgressExecutableStarted());
1205-
isFirstClient = true;
1206-
}
1207-
void this.init(rootUri, isFirstClient).catch(logAndReturn.undefined);
1208-
1209-
} catch (errJS) {
1210-
const err: NodeJS.ErrnoException = errJS as NodeJS.ErrnoException;
1211-
this.isSupported = false; // Running on an OS we don't support yet.
1212-
if (!failureMessageShown) {
1213-
failureMessageShown = true;
1214-
let additionalInfo: string;
1215-
if (err.code === "EPERM") {
1216-
additionalInfo = localize('check.permissions', "EPERM: Check permissions for '{0}'", getLanguageServerFileName());
1217-
} else {
1218-
additionalInfo = String(err);
1194+
let isFirstClient: boolean = false;
1195+
if (!languageClient || languageClientCrashedNeedsRestart) {
1196+
if (languageClientCrashedNeedsRestart) {
1197+
languageClientCrashedNeedsRestart = false;
1198+
// if we're recovering, the isStarted needs to be reset.
1199+
// because we're starting the first client again.
1200+
DefaultClient.isStarted.reset();
1201+
}
1202+
isFirstClient = true;
1203+
firstClientStarted = this.createLanguageClient();
1204+
firstClientStarted.catch((errJS) => {
1205+
const err: NodeJS.ErrnoException = errJS as NodeJS.ErrnoException;
1206+
this.isSupported = false; // Running on an OS we don't support yet.
1207+
if (!failureMessageShown) {
1208+
failureMessageShown = true;
1209+
let additionalInfo: string;
1210+
if (err.code === "EPERM") {
1211+
additionalInfo = localize('check.permissions', "EPERM: Check permissions for '{0}'", getLanguageServerFileName());
1212+
} else {
1213+
additionalInfo = String(err);
1214+
}
1215+
void vscode.window.showErrorMessage(localize("unable.to.start", "Unable to start the C/C++ language server. IntelliSense features will be disabled. Error: {0}", additionalInfo));
12191216
}
1220-
void vscode.window.showErrorMessage(localize("unable.to.start", "Unable to start the C/C++ language server. IntelliSense features will be disabled. Error: {0}", additionalInfo));
1221-
}
1217+
});
1218+
firstClientStarted.then((success) => {
1219+
if (success) {
1220+
util.setProgress(util.getProgressExecutableStarted());
1221+
void this.init(rootUri, isFirstClient).catch(logAndReturn.undefined);
1222+
}
1223+
}, logAndReturn.undefined);
12221224
}
12231225
}
12241226

@@ -1464,7 +1466,7 @@ export class DefaultClient implements Client {
14641466
};
14651467
}
14661468

1467-
private async createLanguageClient(): Promise<void> {
1469+
private async createLanguageClient(): Promise<boolean> {
14681470
const currentCaseSensitiveFileSupport: PersistentWorkspaceState<boolean> = new PersistentWorkspaceState<boolean>("CPP.currentCaseSensitiveFileSupport", false);
14691471
let resetDatabase: boolean = false;
14701472
const serverModule: string = getLanguageServerFileName();
@@ -1580,12 +1582,25 @@ export class DefaultClient implements Client {
15801582
languageClient.onNotification(DebugProtocolNotification, logDebugProtocol);
15811583
languageClient.onNotification(DebugLogNotification, logLocalized);
15821584
languageClient.registerProposedFeatures();
1583-
await languageClient.start();
1585+
1586+
// If thisLanguageClient.start() were to trigger our errorHandler.closed, it may interrupt us
1587+
// (since we're calling await here) and overwrite `languageClient`. Use a new var to ensure we
1588+
// don't attempt to sent multiple initialization messages.
1589+
const thisLanguageClient: LanguageClient = languageClient;
1590+
await thisLanguageClient.start();
1591+
if (thisLanguageClient !== languageClient) {
1592+
return false;
1593+
}
15841594

15851595
// Move initialization to a separate message, so we can see log output from it.
15861596
// A request is used in order to wait for completion and ensure that no subsequent
15871597
// higher priority message may be processed before the Initialization request.
1588-
watchForCrashes(await languageClient.sendRequest(InitializationRequest, cppInitializationParams));
1598+
const crashPath: string = await thisLanguageClient.sendRequest(InitializationRequest, cppInitializationParams);
1599+
if (crashPath) {
1600+
// empty string may be returned in error cases.
1601+
watchForCrashes(crashPath);
1602+
}
1603+
return true;
15891604
}
15901605

15911606
public async sendDidChangeSettings(): Promise<void> {

0 commit comments

Comments
 (0)