Skip to content

Commit 94051ea

Browse files
committed
second try to remove race
1 parent 655d43b commit 94051ea

File tree

2 files changed

+67
-55
lines changed

2 files changed

+67
-55
lines changed

src/client/activation/jedi/languageServerProxy.ts

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import { traceDecoratorError, traceDecoratorVerbose, traceError } from '../../lo
2323
export class JediLanguageServerProxy implements ILanguageServerProxy {
2424
public languageClient: LanguageClient | undefined;
2525

26-
private languageServerTask: Promise<void> | undefined;
26+
private languageDisposeTask: Promise<void> | undefined;
27+
28+
private languageServerTask: Promise<LanguageClient> | undefined;
2729

2830
private readonly disposables: Disposable[] = [];
2931

@@ -54,7 +56,7 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
5456
};
5557

5658
// Do not await on this.
57-
this.languageServerTask = this.languageClient.stop().then(
59+
this.languageDisposeTask = this.languageClient.stop().then(
5860
() => killServer(),
5961
(ex) => {
6062
traceError('Stopping language client failed', ex);
@@ -63,6 +65,7 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
6365
);
6466

6567
this.languageClient = undefined;
68+
this.languageServerTask = undefined;
6669
}
6770

6871
while (this.disposables.length > 0) {
@@ -86,49 +89,52 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
8689
interpreter: PythonEnvironment | undefined,
8790
options: LanguageClientOptions,
8891
): Promise<void> {
89-
if (this.languageServerTask) {
90-
// Make sure to hold onto local client so that
91-
// await below doesn't use wrong client.
92-
const client = this.languageClient;
93-
94-
// Wait if there is pending server task.
95-
await this.languageServerTask;
96-
97-
// if client is already started, bail out.
98-
if (client) {
99-
return;
100-
}
92+
if (this.languageDisposeTask) {
93+
await this.languageDisposeTask;
94+
this.languageDisposeTask = undefined;
10195
}
10296

103-
this.lsVersion =
104-
(options.middleware ? (<LanguageClientMiddleware>options.middleware).serverVersion : undefined) ?? '0.19.3';
105-
106-
this.languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
107-
this.registerHandlers();
97+
if (!this.languageServerTask) {
98+
this.languageServerTask = this.startServer(resource, interpreter, options);
99+
}
108100

109-
this.languageServerTask = this.languageClient.start();
110-
await this.languageServerTask;
101+
this.languageClient = await this.languageDisposeTask;
111102
}
112103

113104
// eslint-disable-next-line class-methods-use-this
114105
public loadExtension(): void {
115106
// No body.
116107
}
117108

109+
private async startServer(
110+
resource: Resource,
111+
interpreter: PythonEnvironment | undefined,
112+
options: LanguageClientOptions,
113+
) {
114+
this.lsVersion =
115+
(options.middleware ? (<LanguageClientMiddleware>options.middleware).serverVersion : undefined) ?? '0.19.3';
116+
117+
const languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
118+
this.registerHandlers(languageClient);
119+
120+
await languageClient.start();
121+
return languageClient;
122+
}
123+
118124
@captureTelemetry(
119125
EventName.JEDI_LANGUAGE_SERVER_READY,
120126
undefined,
121127
true,
122128
undefined,
123129
JediLanguageServerProxy.versionTelemetryProps,
124130
)
125-
private registerHandlers() {
131+
private registerHandlers(languageClient: LanguageClient) {
126132
if (this.disposed) {
127133
// Check if it got disposed in the interim.
128134
return;
129135
}
130136

131-
const progressReporting = new ProgressReporting(this.languageClient!);
137+
const progressReporting = new ProgressReporting(languageClient!);
132138
this.disposables.push(progressReporting);
133139

134140
this.disposables.push(
@@ -137,7 +143,7 @@ export class JediLanguageServerProxy implements ILanguageServerProxy {
137143
// the workspace configurations (to then pick up pythonPath set in the middleware).
138144
// This is needed as interpreter changes via the interpreter path service happen
139145
// outside of VS Code's settings (which would mean VS Code sends the config updates itself).
140-
this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, {
146+
languageClient.sendNotification(DidChangeConfigurationNotification.type, {
141147
settings: null,
142148
});
143149
}),

src/client/activation/node/languageServerProxy.ts

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ namespace GetExperimentValue {
5151
export class NodeLanguageServerProxy implements ILanguageServerProxy {
5252
public languageClient: LanguageClient | undefined;
5353

54-
private languageServerTask: Promise<void> | undefined;
54+
private languageDisposeTask: Promise<void> | undefined;
55+
56+
private languageServerTask: Promise<LanguageClient> | undefined;
5557

5658
private cancellationStrategy: FileBasedCancellationStrategy | undefined;
5759

@@ -80,11 +82,12 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
8082
public dispose(): void {
8183
if (this.languageClient) {
8284
// Do not await on this.
83-
this.languageServerTask = this.languageClient
85+
this.languageDisposeTask = this.languageClient
8486
.stop()
8587
.then(noop, (ex) => traceError('Stopping language client failed', ex));
8688

8789
this.languageClient = undefined;
90+
this.languageServerTask = undefined;
8891
}
8992
if (this.cancellationStrategy) {
9093
this.cancellationStrategy.dispose();
@@ -110,42 +113,45 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
110113
interpreter: PythonEnvironment | undefined,
111114
options: LanguageClientOptions,
112115
): Promise<void> {
113-
if (this.languageServerTask) {
114-
// Make sure to hold onto local client so that
115-
// await below doesn't use wrong client.
116-
const client = this.languageClient;
117-
118-
// Wait if there is pending server task.
119-
await this.languageServerTask;
120-
121-
// if client is already started, bail out.
122-
if (client) {
123-
return;
124-
}
116+
if (this.languageDisposeTask) {
117+
await this.languageDisposeTask;
118+
this.languageDisposeTask = undefined;
119+
}
120+
121+
if (!this.languageServerTask) {
122+
this.languageServerTask = this.startServer(resource, interpreter, options);
125123
}
126124

125+
this.languageClient = await this.languageDisposeTask;
126+
}
127+
128+
// eslint-disable-next-line class-methods-use-this
129+
public loadExtension(): void {
130+
// No body.
131+
}
132+
133+
private async startServer(
134+
resource: Resource,
135+
interpreter: PythonEnvironment | undefined,
136+
options: LanguageClientOptions,
137+
) {
127138
const extension = this.extensions.getExtension(PYLANCE_EXTENSION_ID);
128139
this.lsVersion = extension?.packageJSON.version || '0';
129140

130141
this.cancellationStrategy = new FileBasedCancellationStrategy();
131142
options.connectionOptions = { cancellationStrategy: this.cancellationStrategy };
132143

133-
this.languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
134-
this.registerHandlers(resource);
144+
const languageClient = await this.factory.createLanguageClient(resource, interpreter, options);
145+
this.registerHandlers(languageClient, resource);
135146

136147
this.disposables.push(
137148
this.workspace.onDidGrantWorkspaceTrust(() => {
138-
this.languageClient!.sendNotification('python/workspaceTrusted', { isTrusted: true });
149+
languageClient.sendNotification('python/workspaceTrusted', { isTrusted: true });
139150
}),
140151
);
141152

142-
this.languageServerTask = this.languageClient.start();
143-
await this.languageServerTask;
144-
}
145-
146-
// eslint-disable-next-line class-methods-use-this
147-
public loadExtension(): void {
148-
// No body.
153+
await languageClient.start();
154+
return languageClient;
149155
}
150156

151157
@captureTelemetry(
@@ -155,13 +161,13 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
155161
undefined,
156162
NodeLanguageServerProxy.versionTelemetryProps,
157163
)
158-
private registerHandlers(_resource: Resource) {
164+
private registerHandlers(languageClient: LanguageClient, _resource: Resource) {
159165
if (this.disposed) {
160166
// Check if it got disposed in the interim.
161167
return;
162168
}
163169

164-
const progressReporting = new ProgressReporting(this.languageClient!);
170+
const progressReporting = new ProgressReporting(languageClient);
165171
this.disposables.push(progressReporting);
166172

167173
this.disposables.push(
@@ -170,28 +176,28 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
170176
// the workspace configurations (to then pick up pythonPath set in the middleware).
171177
// This is needed as interpreter changes via the interpreter path service happen
172178
// outside of VS Code's settings (which would mean VS Code sends the config updates itself).
173-
this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, {
179+
languageClient.sendNotification(DidChangeConfigurationNotification.type, {
174180
settings: null,
175181
});
176182
}),
177183
);
178184
this.disposables.push(
179185
this.environmentService.onDidEnvironmentVariablesChange(() => {
180-
this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, {
186+
languageClient.sendNotification(DidChangeConfigurationNotification.type, {
181187
settings: null,
182188
});
183189
}),
184190
);
185191

186-
this.languageClient!.onRequest(
192+
languageClient.onRequest(
187193
InExperiment.Method,
188194
async (params: InExperiment.IRequest): Promise<InExperiment.IResponse> => {
189195
const inExperiment = await this.experimentService.inExperiment(params.experimentName);
190196
return { inExperiment };
191197
},
192198
);
193199

194-
this.languageClient!.onRequest(
200+
languageClient.onRequest(
195201
GetExperimentValue.Method,
196202
async <T extends boolean | number | string>(
197203
params: GetExperimentValue.IRequest,
@@ -202,7 +208,7 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy {
202208
);
203209

204210
this.disposables.push(
205-
this.languageClient!.onRequest('python/isTrustedWorkspace', async () => ({
211+
languageClient.onRequest('python/isTrustedWorkspace', async () => ({
206212
isTrusted: this.workspace.isTrusted,
207213
})),
208214
);

0 commit comments

Comments
 (0)