Skip to content

Commit 90fbd0e

Browse files
committed
Extract cancellation.electron
This makes it possible to replace the cancellation logic for serverless
1 parent 3b9db3d commit 90fbd0e

9 files changed

Lines changed: 95 additions & 45 deletions

File tree

extensions/typescript-language-features/src/extension.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66
import * as rimraf from 'rimraf';
77
import * as vscode from 'vscode';
8-
import { NodeLogDirectoryProvider } from './utils/logDirectoryProvider.electron';
98
import { Api, getExtensionApi } from './api';
109
import { registerCommands } from './commands/index';
1110
import { LanguageConfigurationManager } from './features/languageConfiguration';
1211
import * as task from './features/task';
1312
import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost';
13+
import { NodeRequestCanceller } from './tsServer/cancellation.electron';
1414
import { CommandManager } from './utils/commandManager';
1515
import * as electron from './utils/electron';
16+
import { NodeLogDirectoryProvider } from './utils/logDirectoryProvider.electron';
1617
import { PluginManager } from './utils/plugins';
1718

1819
export function activate(
@@ -29,7 +30,9 @@ export function activate(
2930

3031
const logDirectoryProvider = new NodeLogDirectoryProvider(context);
3132

32-
const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, logDirectoryProvider, item => {
33+
const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, logDirectoryProvider, {
34+
create: (kind, tracer) => new NodeRequestCanceller(kind, tracer)
35+
}, item => {
3336
onCompletionAccepted.fire(item);
3437
});
3538

extensions/typescript-language-features/src/lazyClientHost.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as vscode from 'vscode';
7+
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
78
import TypeScriptServiceClientHost from './typeScriptServiceClientHost';
89
import { flatten } from './utils/arrays';
910
import { CommandManager } from './utils/commandManager';
@@ -20,6 +21,7 @@ export function createLazyClientHost(
2021
pluginManager: PluginManager,
2122
commandManager: CommandManager,
2223
logDirectoryProvider: ILogDirectoryProvider,
24+
cancellerFactory: OngoingRequestCancellerFactory,
2325
onCompletionAccepted: (item: vscode.CompletionItem) => void,
2426
): Lazy<TypeScriptServiceClientHost> {
2527
return lazy(() => {
@@ -29,6 +31,7 @@ export function createLazyClientHost(
2931
pluginManager,
3032
commandManager,
3133
logDirectoryProvider,
34+
cancellerFactory,
3235
onCompletionAccepted);
3336

3437
context.subscriptions.push(clientHost);

extensions/typescript-language-features/src/test/server.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import * as assert from 'assert';
77
import 'mocha';
88
import * as stream from 'stream';
9-
import { PipeRequestCanceller, TsServerProcess, ProcessBasedTsServer } from '../tsServer/server';
9+
import type * as Proto from '../protocol';
10+
import { NodeRequestCanceller } from '../tsServer/cancellation.electron';
11+
import { ProcessBasedTsServer, TsServerProcess } from '../tsServer/server';
1012
import { nulToken } from '../utils/cancellation';
1113
import Logger from '../utils/logger';
1214
import { TelemetryReporter } from '../utils/telemetry';
1315
import Tracer from '../utils/tracer';
14-
import type * as Proto from '../protocol';
1516

1617

1718
const NoopTelemetryReporter = new class implements TelemetryReporter {
@@ -63,7 +64,7 @@ suite('Server', () => {
6364

6465
test('should send requests with increasing sequence numbers', async () => {
6566
const process = new FakeServerProcess();
66-
const server = new ProcessBasedTsServer('semantic', process, undefined, new PipeRequestCanceller('semantic', undefined, tracer), undefined!, NoopTelemetryReporter, tracer);
67+
const server = new ProcessBasedTsServer('semantic', process, undefined, new NodeRequestCanceller('semantic', tracer), undefined!, NoopTelemetryReporter, tracer);
6768

6869
const onWrite1 = process.onWrite();
6970
server.executeImpl('geterr', {}, { isAsync: false, token: nulToken, expectsResult: true });
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as fs from 'fs';
7+
import { OngoingRequestCanceller } from './cancellation';
8+
import { getTempFile } from '../utils/electron';
9+
import Tracer from '../utils/tracer';
10+
11+
export class NodeRequestCanceller implements OngoingRequestCanceller {
12+
public readonly cancellationPipeName: string;
13+
14+
public constructor(
15+
private readonly _serverId: string,
16+
private readonly _tracer: Tracer,
17+
) {
18+
this.cancellationPipeName = getTempFile('tscancellation');
19+
}
20+
21+
public tryCancelOngoingRequest(seq: number): boolean {
22+
if (!this.cancellationPipeName) {
23+
return false;
24+
}
25+
this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`);
26+
try {
27+
fs.writeFileSync(this.cancellationPipeName + seq, '');
28+
} catch {
29+
// noop
30+
}
31+
return true;
32+
}
33+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import Tracer from '../utils/tracer';
7+
8+
export interface OngoingRequestCanceller {
9+
readonly cancellationPipeName: string | undefined;
10+
tryCancelOngoingRequest(seq: number): boolean;
11+
}
12+
13+
export interface OngoingRequestCancellerFactory {
14+
create(serverId: string, tracer: Tracer): OngoingRequestCanceller;
15+
}
16+
17+
export const noopRequestCanceller = new class implements OngoingRequestCanceller {
18+
public readonly cancellationPipeName = undefined;
19+
20+
public tryCancelOngoingRequest(_seq: number): boolean {
21+
return false;
22+
}
23+
};

extensions/typescript-language-features/src/tsServer/server.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import * as fs from 'fs';
76
import * as vscode from 'vscode';
87
import type * as Proto from '../protocol';
98
import { EventName } from '../protocol.const';
109
import { CallbackMap } from '../tsServer/callbackMap';
10+
import { OngoingRequestCanceller } from './cancellation';
1111
import { RequestItem, RequestQueue, RequestQueueingType } from '../tsServer/requestQueue';
1212
import { TypeScriptServerError } from '../tsServer/serverError';
1313
import { ServerResponse, TypeScriptRequests } from '../typescriptService';
@@ -16,30 +16,6 @@ import { TelemetryReporter } from '../utils/telemetry';
1616
import Tracer from '../utils/tracer';
1717
import { TypeScriptVersion } from '../utils/versionProvider';
1818

19-
export interface OngoingRequestCanceller {
20-
tryCancelOngoingRequest(seq: number): boolean;
21-
}
22-
23-
export class PipeRequestCanceller implements OngoingRequestCanceller {
24-
public constructor(
25-
private readonly _serverId: string,
26-
private readonly _cancellationPipeName: string | undefined,
27-
private readonly _tracer: Tracer,
28-
) { }
29-
30-
public tryCancelOngoingRequest(seq: number): boolean {
31-
if (!this._cancellationPipeName) {
32-
return false;
33-
}
34-
this._tracer.logTrace(this._serverId, `TypeScript Server: trying to cancel ongoing request with sequence number ${seq}`);
35-
try {
36-
fs.writeFileSync(this._cancellationPipeName + seq, '');
37-
} catch {
38-
// noop
39-
}
40-
return true;
41-
}
42-
}
4319

4420
export interface ITypeScriptServer {
4521
readonly onEvent: vscode.Event<Proto.Event>;

extensions/typescript-language-features/src/tsServer/spawner.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import * as path from 'path';
77
import * as vscode from 'vscode';
8+
import { OngoingRequestCancellerFactory } from '../tsServer/cancellation';
89
import { ClientCapabilities, ClientCapability } from '../typescriptService';
910
import API from '../utils/api';
1011
import { SeparateSyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration';
@@ -17,7 +18,7 @@ import { ChildServerProcess } from '../utils/serverProcess';
1718
import { TelemetryReporter } from '../utils/telemetry';
1819
import Tracer from '../utils/tracer';
1920
import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider';
20-
import { GetErrRoutingTsServer, ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerDelegate } from './server';
21+
import { GetErrRoutingTsServer, ITypeScriptServer, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerDelegate } from './server';
2122

2223
const enum ServerKind {
2324
Main = 'main',
@@ -55,6 +56,7 @@ export class TypeScriptServerSpawner {
5556
capabilities: ClientCapabilities,
5657
configuration: TypeScriptServiceConfiguration,
5758
pluginManager: PluginManager,
59+
cancellerFactory: OngoingRequestCancellerFactory,
5860
delegate: TsServerDelegate,
5961
): ITypeScriptServer {
6062
let primaryServer: ITypeScriptServer;
@@ -65,26 +67,26 @@ export class TypeScriptServerSpawner {
6567
{
6668
const enableDynamicRouting = serverType === CompositeServerType.DynamicSeparateSyntax;
6769
primaryServer = new SyntaxRoutingTsServer({
68-
syntax: this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager),
69-
semantic: this.spawnTsServer(ServerKind.Semantic, version, configuration, pluginManager)
70+
syntax: this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager, cancellerFactory),
71+
semantic: this.spawnTsServer(ServerKind.Semantic, version, configuration, pluginManager, cancellerFactory),
7072
}, delegate, enableDynamicRouting);
7173
break;
7274
}
7375
case CompositeServerType.Single:
7476
{
75-
primaryServer = this.spawnTsServer(ServerKind.Main, version, configuration, pluginManager);
77+
primaryServer = this.spawnTsServer(ServerKind.Main, version, configuration, pluginManager, cancellerFactory);
7678
break;
7779
}
7880
case CompositeServerType.SyntaxOnly:
7981
{
80-
primaryServer = this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager);
82+
primaryServer = this.spawnTsServer(ServerKind.Syntax, version, configuration, pluginManager, cancellerFactory);
8183
break;
8284
}
8385
}
8486

8587
if (this.shouldUseSeparateDiagnosticsServer(configuration)) {
8688
return new GetErrRoutingTsServer({
87-
getErr: this.spawnTsServer(ServerKind.Diagnostics, version, configuration, pluginManager),
89+
getErr: this.spawnTsServer(ServerKind.Diagnostics, version, configuration, pluginManager, cancellerFactory),
8890
primary: primaryServer,
8991
}, delegate);
9092
}
@@ -126,10 +128,12 @@ export class TypeScriptServerSpawner {
126128
version: TypeScriptVersion,
127129
configuration: TypeScriptServiceConfiguration,
128130
pluginManager: PluginManager,
131+
cancellerFactory: OngoingRequestCancellerFactory,
129132
): ITypeScriptServer {
130133
const apiVersion = version.apiVersion || API.defaultVersion;
131134

132-
const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager);
135+
const canceller = cancellerFactory.create(kind, this._tracer);
136+
const { args, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager, canceller.cancellationPipeName);
133137

134138
if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) {
135139
if (tsServerLogFile) {
@@ -147,7 +151,7 @@ export class TypeScriptServerSpawner {
147151
kind,
148152
new ChildServerProcess(childProcess),
149153
tsServerLogFile,
150-
new PipeRequestCanceller(kind, cancellationPipeName, this._tracer),
154+
canceller,
151155
version,
152156
this._telemetryReporter,
153157
this._tracer);
@@ -171,7 +175,8 @@ export class TypeScriptServerSpawner {
171175
currentVersion: TypeScriptVersion,
172176
apiVersion: API,
173177
pluginManager: PluginManager,
174-
): { args: string[], cancellationPipeName: string, tsServerLogFile: string | undefined } {
178+
cancellationPipeName: string | undefined,
179+
): { args: string[], tsServerLogFile: string | undefined } {
175180
const args: string[] = [];
176181
let tsServerLogFile: string | undefined;
177182

@@ -193,8 +198,9 @@ export class TypeScriptServerSpawner {
193198
args.push('--enableTelemetry');
194199
}
195200

196-
const cancellationPipeName = electron.getTempFile('tscancellation');
197-
args.push('--cancellationPipeName', cancellationPipeName + '*');
201+
if (cancellationPipeName) {
202+
args.push('--cancellationPipeName', cancellationPipeName + '*');
203+
}
198204

199205
if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) {
200206
const logDir = this._logDirectoryProvider.getNewLogDirectory();
@@ -238,7 +244,7 @@ export class TypeScriptServerSpawner {
238244
args.push('--validateDefaultNpmLocation');
239245
}
240246

241-
return { args, cancellationPipeName, tsServerLogFile };
247+
return { args, tsServerLogFile };
242248
}
243249

244250
private static getDebugPort(kind: ServerKind): number | undefined {

extensions/typescript-language-features/src/typeScriptServiceClientHost.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@
99
* ------------------------------------------------------------------------------------------ */
1010

1111
import * as vscode from 'vscode';
12-
import { ILogDirectoryProvider } from './utils/logDirectoryProvider';
1312
import { DiagnosticKind } from './features/diagnostics';
1413
import FileConfigurationManager from './features/fileConfigurationManager';
1514
import LanguageProvider from './languageProvider';
1615
import * as Proto from './protocol';
1716
import * as PConst from './protocol.const';
17+
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
1818
import TypeScriptServiceClient from './typescriptServiceClient';
1919
import { coalesce, flatten } from './utils/arrays';
2020
import { CommandManager } from './utils/commandManager';
2121
import { Disposable } from './utils/dispose';
2222
import * as errorCodes from './utils/errorCodes';
2323
import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription';
24+
import { ILogDirectoryProvider } from './utils/logDirectoryProvider';
2425
import { PluginManager } from './utils/plugins';
2526
import * as typeConverters from './utils/typeConverters';
2627
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
@@ -61,6 +62,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
6162
pluginManager: PluginManager,
6263
private readonly commandManager: CommandManager,
6364
logDirectoryProvider: ILogDirectoryProvider,
65+
cancellerFactory: OngoingRequestCancellerFactory,
6466
onCompletionAccepted: (item: vscode.CompletionItem) => void,
6567
) {
6668
super();
@@ -70,6 +72,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
7072
workspaceState,
7173
pluginManager,
7274
logDirectoryProvider,
75+
cancellerFactory,
7376
allModeIds));
7477

7578
this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => {

extensions/typescript-language-features/src/typescriptServiceClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as fs from 'fs';
77
import * as path from 'path';
88
import * as vscode from 'vscode';
99
import * as nls from 'vscode-nls';
10-
import { onCaseInsenitiveFileSystem } from './utils/fileSystem';
10+
import { OngoingRequestCancellerFactory } from './tsServer/cancellation';
1111
import BufferSyncSupport from './features/bufferSyncSupport';
1212
import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics';
1313
import * as Proto from './protocol';
@@ -20,6 +20,7 @@ import API from './utils/api';
2020
import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration';
2121
import { Disposable } from './utils/dispose';
2222
import * as fileSchemes from './utils/fileSchemes';
23+
import { onCaseInsenitiveFileSystem } from './utils/fileSystem';
2324
import { ILogDirectoryProvider } from './utils/logDirectoryProvider';
2425
import Logger from './utils/logger';
2526
import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider';
@@ -125,6 +126,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
125126
private readonly workspaceState: vscode.Memento,
126127
public readonly pluginManager: PluginManager,
127128
private readonly logDirectoryProvider: ILogDirectoryProvider,
129+
private readonly cancellerFactory: OngoingRequestCancellerFactory,
128130
allModeIds: readonly string[]
129131
) {
130132
super();
@@ -348,7 +350,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType
348350

349351
const apiVersion = version.apiVersion || API.defaultVersion;
350352
let mytoken = ++this.token;
351-
const handle = this.typescriptServerSpawner.spawn(version, this.capabilities, this.configuration, this.pluginManager, {
353+
const handle = this.typescriptServerSpawner.spawn(version, this.capabilities, this.configuration, this.pluginManager, this.cancellerFactory, {
352354
onFatalError: (command, err) => this.fatalError(command, err),
353355
});
354356
this.serverState = new ServerState.Running(handle, apiVersion, undefined, true);

0 commit comments

Comments
 (0)