Skip to content

Commit 03ffd24

Browse files
committed
Clean up socket lifecycle. Remove unused socket wrappers.
1 parent a5c23aa commit 03ffd24

File tree

9 files changed

+297
-439
lines changed

9 files changed

+297
-439
lines changed

src/vs/base/parts/ipc/node/ipc.cp.ts

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { ChildProcess, fork, ForkOptions } from 'child_process';
6+
import { ChildProcess, fork, ForkOptions, SendHandle } from 'child_process';
77
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
88
import { Delayer, createCancelablePromise } from 'vs/base/common/async';
99
import { deepClone } from 'vs/base/common/objects';
1010
import { Emitter, Event } from 'vs/base/common/event';
1111
import { createQueuedSender } from 'vs/base/node/processes';
1212
import { IChannel, ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient } from 'vs/base/parts/ipc/common/ipc';
13-
import { isRemoteConsoleLog, log } from 'vs/base/common/console';
13+
import { IRemoteConsoleLog, isRemoteConsoleLog, log } from 'vs/base/common/console';
1414
import { CancellationToken } from 'vs/base/common/cancellation';
1515
import * as errors from 'vs/base/common/errors';
1616
import { VSBuffer } from 'vs/base/common/buffer';
1717
import { isMacintosh } from 'vs/base/common/platform';
18+
// eslint-disable-next-line code-import-patterns
19+
import { IExtHostMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
1820

1921
/**
2022
* This implementation doesn't perform well since it uses base64 encoding for buffers.
@@ -290,3 +292,101 @@ export class Client implements IChannelClient, IDisposable {
290292
this.activeRequests.clear();
291293
}
292294
}
295+
296+
export type ExtensionHostMessage = IRemoteConsoleLog | IExtHostReadyMessage;
297+
export class ExtensionHost implements IDisposable {
298+
private child: ChildProcess | null = null;
299+
private readonly _onDidProcessExit = new Emitter<{ code: number, signal: string }>();
300+
public readonly onDidProcessExit = this._onDidProcessExit.event;
301+
302+
private readonly _onReadyMessage = new Emitter<IExtHostReadyMessage>();
303+
public readonly onReady = this._onReadyMessage.event;
304+
305+
private disposeClient() {
306+
if (this.child) {
307+
this.child.kill();
308+
this.child = null;
309+
}
310+
}
311+
312+
dispose() {
313+
this._onDidProcessExit.dispose();
314+
315+
this.disposeClient();
316+
}
317+
318+
sendIPCMessage(message: IExtHostMessage, sendHandle?: SendHandle) {
319+
if (this.child && this.child.connected) {
320+
return this.child.send(message, sendHandle);
321+
}
322+
323+
return false;
324+
}
325+
326+
constructor(private modulePath: string, private options: IIPCOptions) {
327+
const args = options && options.args ? this.options.args : [];
328+
const forkOpts: ForkOptions = Object.create(null);
329+
330+
forkOpts.env = { ...deepClone(process.env), 'VSCODE_PARENT_PID': String(process.pid) };
331+
332+
if (this.options && this.options.env) {
333+
forkOpts.env = { ...forkOpts.env, ...this.options.env };
334+
}
335+
336+
if (this.options && this.options.freshExecArgv) {
337+
forkOpts.execArgv = [];
338+
}
339+
340+
if (this.options && typeof this.options.debug === 'number') {
341+
forkOpts.execArgv = ['--nolazy', '--inspect=' + this.options.debug];
342+
}
343+
344+
if (this.options && typeof this.options.debugBrk === 'number') {
345+
forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk];
346+
}
347+
348+
if (forkOpts.execArgv === undefined) {
349+
// if not set, the forked process inherits the execArgv of the parent process
350+
// --inspect and --inspect-brk can not be inherited as the port would conflict
351+
forkOpts.execArgv = process.execArgv.filter(a => !/^--inspect(-brk)?=/.test(a)); // remove
352+
}
353+
354+
if (isMacintosh && forkOpts.env) {
355+
// Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes
356+
// See https://github.com/microsoft/vscode/issues/105848
357+
delete forkOpts.env['DYLD_LIBRARY_PATH'];
358+
}
359+
360+
this.child = fork(this.modulePath, args, forkOpts);
361+
362+
const onRawMessage = Event.fromNodeEventEmitter<ExtensionHostMessage>(this.child, 'message', msg => msg);
363+
364+
onRawMessage(msg => {
365+
// Handle remote console logs specially
366+
if (isRemoteConsoleLog(msg)) {
367+
log(msg, `IPC Library: ${this.options.serverName}`);
368+
return;
369+
}
370+
371+
if (msg.type === 'VSCODE_EXTHOST_IPC_READY') {
372+
this._onReadyMessage.fire(msg);
373+
}
374+
});
375+
376+
const onExit = () => this.disposeClient();
377+
process.once('exit', onExit);
378+
379+
this.child.on('error', err => console.warn('IPC "' + this.options.serverName + '" errored with ' + err));
380+
381+
this.child.on('exit', (code: any, signal: any) => {
382+
process.removeListener('exit' as 'loaded', onExit); // https://github.com/electron/electron/issues/21475
383+
384+
385+
if (code !== 0 && signal !== 'SIGTERM') {
386+
console.warn('IPC "' + this.options.serverName + '" crashed with exit code ' + code + ' and signal ' + signal);
387+
}
388+
389+
this._onDidProcessExit.fire({ code, signal });
390+
});
391+
}
392+
}

src/vs/platform/remote/node/serverSocket.ts

Lines changed: 0 additions & 207 deletions
This file was deleted.

src/vs/server/connection/abstractConnection.ts

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

6+
import { onUnexpectedError } from 'vs/base/common/errors';
67
import { Emitter } from 'vs/base/common/event';
78
import { ConsoleLogger } from 'vs/platform/log/common/log';
89
import { ServerProtocol } from 'vs/server/protocol';
@@ -17,39 +18,50 @@ export abstract class AbstractConnection {
1718
private disposed = false;
1819
private _offline: number | undefined;
1920

20-
protected readonly logger: ConsoleLogger;
21+
protected readonly logService: ConsoleLogger;
2122

2223
public constructor(
2324
protected readonly protocol: ServerProtocol,
2425
public readonly name: string,
2526
) {
26-
this.logger = new ConsoleLogger();
27+
this.logService = new ConsoleLogger();
2728

28-
this.logger.debug('Connecting...');
29-
this.onClose(() => this.logger.debug('Closed'));
29+
this.logService.debug('Connecting...');
30+
this.onClose(() => this.logService.debug('Closed'));
3031
}
3132

3233
public get offline(): number | undefined {
3334
return this._offline;
3435
}
3536

3637
public reconnect(protocol: ServerProtocol): void {
37-
this.logger.debug(`${this.protocol.reconnectionToken} Reconnecting...`);
38+
this.logService.debug(`${this.protocol.reconnectionToken} Reconnecting...`);
3839
this._offline = undefined;
3940
this.doReconnect(protocol);
4041
}
4142

4243
public dispose(reason?: string): void {
43-
this.logger.debug(`${this.protocol.reconnectionToken} Disposing...`, reason);
44+
this.logService.debug(`${this.protocol.reconnectionToken} Disposing...`, reason);
4445
if (!this.disposed) {
4546
this.disposed = true;
4647
this.doDispose();
4748
this._onClose.fire();
4849
}
4950
}
5051

52+
public safeDisposeProtocolAndSocket(): void {
53+
try {
54+
this.protocol.acceptDisconnect();
55+
const socket = this.protocol.getSocket();
56+
this.protocol.dispose();
57+
socket.dispose();
58+
} catch (err) {
59+
onUnexpectedError(err);
60+
}
61+
}
62+
5163
protected setOffline(): void {
52-
this.logger.debug('Disconnected');
64+
this.logService.debug('Disconnected');
5365
if (!this._offline) {
5466
this._offline = Date.now();
5567
}

0 commit comments

Comments
 (0)