提交 8a6b8fd5 编写于 作者: S Sandeep Somavarapu

Clean up thred service

上级 cf6b4b56
......@@ -132,7 +132,13 @@ class ExtensionHostProcessManager {
PIPE_LOGGING: 'true',
VERBOSE_LOGGING: true,
VSCODE_WINDOW_ID: String(this.windowService.getWindowId())
})
}),
// We only detach the extension host on windows. Linux and Mac orphan by default
// and detach under Linux and Mac create another process group.
// We detach because we have noticed that when the renderer exits, its child processes
// (i.e. extension host) is taken down in a brutal fashion by the OS
detached: !!isWindows,
onExtensionHostMessage
};
// Help in case we fail to start it
......@@ -145,20 +151,16 @@ class ExtensionHostProcessManager {
}
// Initialize extension host process with hand shakes
this.initializeExtensionHostProcess = new TPromise<ChildProcess>((c, e) => {
this.initializeExtensionHostProcess = this.doInitializeExtensionHostProcess(opts);
}
private doInitializeExtensionHostProcess(opts: any): TPromise<ChildProcess> {
return new TPromise<ChildProcess>((c, e) => {
// Resolve additional execution args (e.g. debug)
return this.resolveDebugPort(this.environmentService.debugExtensionHost.port, port => {
this.resolveDebugPort(this.environmentService.debugExtensionHost.port).then(port => {
if (port) {
opts.execArgv = ['--nolazy', (this.isExtensionDevelopmentDebugging ? '--debug-brk=' : '--debug=') + port];
}
// We only detach the extension host on windows. Linux and Mac orphan by default
// and detach under Linux and Mac create another process group.
if (isWindows) {
// We detach because we have noticed that when the renderer exits, its child processes
// (i.e. extension host) is taken down in a brutal fashion by the OS
opts.detached = true;
}
// Run Extension Host as fork of current process
this.extensionHostProcessHandle = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts);
......@@ -172,159 +174,165 @@ class ExtensionHostProcessManager {
}
// Messages from Extension host
this.extensionHostProcessHandle.on('message', (msg) => {
// 1) Host is ready to receive messages, initialize it
if (msg === 'ready') {
if (this.initializeTimer) {
window.clearTimeout(this.initializeTimer);
}
let initPayload = stringify({
parentPid: process.pid,
environment: {
appSettingsHome: this.environmentService.appSettingsHome,
disableExtensions: this.environmentService.disableExtensions,
userExtensionsHome: this.environmentService.extensionsPath,
extensionDevelopmentPath: this.environmentService.extensionDevelopmentPath,
extensionTestsPath: this.environmentService.extensionTestsPath
},
contextService: {
workspace: this.contextService.getWorkspace()
}
});
this.extensionHostProcessHandle.send(initPayload);
}
// 2) Host is initialized
else if (msg === 'initialized') {
this.unsentMessages.forEach(m => this.postMessage(m));
this.unsentMessages = [];
this.extensionHostProcessReady = true;
this.extensionHostProcessHandle.on('message', msg => {
if (this.onMessaage(msg, opts.onExtensionHostMessage)) {
c(this.extensionHostProcessHandle);
}
// Support logging from extension host
else if (msg && (<ILogEntry>msg).type === '__$console') {
let logEntry: ILogEntry = msg;
let args = [];
try {
let parsed = JSON.parse(logEntry.arguments);
args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
} catch (error) {
args.push(logEntry.arguments);
}
// If the first argument is a string, check for % which indicates that the message
// uses substitution for variables. In this case, we cannot just inject our colored
// [Extension Host] to the front because it breaks substitution.
let consoleArgs = [];
if (typeof args[0] === 'string' && args[0].indexOf('%') >= 0) {
consoleArgs = [`%c[Extension Host]%c ${args[0]}`, 'color: blue', 'color: black', ...args.slice(1)];
} else {
consoleArgs = ['%c[Extension Host]', 'color: blue', ...args];
}
// Send to local console unless we run tests from cli
if (!this.isExtensionDevelopmentTestFromCli) {
console[logEntry.severity].apply(console, consoleArgs);
}
// Log on main side if running tests from cli
if (this.isExtensionDevelopmentTestFromCli) {
ipc.send('vscode:log', logEntry);
}
// Broadcast to other windows if we are in development mode
else if (!this.environmentService.isBuilt || this.isExtensionDevelopmentHost) {
this.windowService.broadcast({
channel: EXTENSION_LOG_BROADCAST_CHANNEL,
payload: logEntry
}, this.environmentService.extensionDevelopmentPath /* target */);
}
}
// Any other message goes to the callback
else {
onExtensionHostMessage(msg);
}
});
// Lifecycle
let onExit = () => this.terminate();
process.once('exit', onExit);
this.extensionHostProcessHandle.on('error', (err) => {
let errorMessage = toErrorMessage(err);
if (errorMessage === this.lastExtensionHostError) {
return; // prevent error spam
}
this.lastExtensionHostError = errorMessage;
this.messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage));
});
this.extensionHostProcessHandle.on('exit', (code: any, signal: any) => {
process.removeListener('exit', onExit);
if (!this.terminating) {
// Unexpected termination
if (!this.isExtensionDevelopmentHost) {
this.messageService.show(Severity.Error, {
message: nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."),
actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL)]
});
console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
}
// Expected development extension termination: When the extension host goes down we also shutdown the window
else if (!this.isExtensionDevelopmentTestFromCli) {
this.windowService.getWindow().close();
}
// When CLI testing make sure to exit with proper exit code
else {
ipc.send('vscode:exit', code);
}
}
});
this.extensionHostProcessHandle.on('error', (err) => this.onError(err));
this.extensionHostProcessHandle.on('exit', (code: any, signal: any) => this.onExit(code, signal, onExit));
});
}, () => this.terminate());
}
private resolveDebugPort(extensionHostPort: number, clb: (port: number) => void): void {
// Check for a free debugging port
if (typeof extensionHostPort === 'number') {
return findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => {
private resolveDebugPort(extensionHostPort: number): TPromise<number> {
if (typeof extensionHostPort !== 'number') {
return TPromise.wrap(void 0);
}
return new TPromise<number>((c, e) => {
findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => {
if (!port) {
console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color: black');
return clb(void 0);
c(void 0);
}
if (port !== extensionHostPort) {
console.warn('%c[Extension Host] %cProvided debugging port ' + extensionHostPort + ' is not free, using ' + port + ' instead.', 'color: blue', 'color: black');
}
if (this.isExtensionDevelopmentDebugging) {
console.warn('%c[Extension Host] %cSTOPPED on first line for debugging on port ' + port, 'color: blue', 'color: black');
} else {
console.info('%c[Extension Host] %cdebugger listening on port ' + port, 'color: blue', 'color: black');
}
return clb(port);
return c(port);
});
});
}
// @return `true` if ready
private onMessaage(msg : any, onExtensionHostMessage : (msg: any) => void): boolean {
// 1) Host is ready to receive messages, initialize it
if (msg === 'ready') {
this.initializeExtensionHost();
return false;
}
// 2) Host is initialized
if (msg === 'initialized') {
this.unsentMessages.forEach(m => this.postMessage(m));
this.unsentMessages = [];
this.extensionHostProcessReady = true;
return true;
}
// Support logging from extension host
if (msg && (<ILogEntry>msg).type === '__$console') {
this.logExtensionHostMessage(<ILogEntry>msg);
return false;
}
// Any other message goes to the callback
onExtensionHostMessage(msg);
return false;
}
private initializeExtensionHost() {
if (this.initializeTimer) {
window.clearTimeout(this.initializeTimer);
}
// Nothing to do here
else {
return clb(void 0);
let initPayload = stringify({
parentPid: process.pid,
environment: {
appSettingsHome: this.environmentService.appSettingsHome,
disableExtensions: this.environmentService.disableExtensions,
userExtensionsHome: this.environmentService.extensionsPath,
extensionDevelopmentPath: this.environmentService.extensionDevelopmentPath,
extensionTestsPath: this.environmentService.extensionTestsPath
},
contextService: {
workspace: this.contextService.getWorkspace()
}
});
this.extensionHostProcessHandle.send(initPayload);
}
private logExtensionHostMessage(logEntry: ILogEntry) {
let args = [];
try {
let parsed = JSON.parse(logEntry.arguments);
args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
} catch (error) {
args.push(logEntry.arguments);
}
// If the first argument is a string, check for % which indicates that the message
// uses substitution for variables. In this case, we cannot just inject our colored
// [Extension Host] to the front because it breaks substitution.
let consoleArgs = [];
if (typeof args[0] === 'string' && args[0].indexOf('%') >= 0) {
consoleArgs = [`%c[Extension Host]%c ${args[0]}`, 'color: blue', 'color: black', ...args.slice(1)];
} else {
consoleArgs = ['%c[Extension Host]', 'color: blue', ...args];
}
// Send to local console unless we run tests from cli
if (!this.isExtensionDevelopmentTestFromCli) {
console[logEntry.severity].apply(console, consoleArgs);
}
// Log on main side if running tests from cli
if (this.isExtensionDevelopmentTestFromCli) {
ipc.send('vscode:log', logEntry);
}
// Broadcast to other windows if we are in development mode
else if (!this.environmentService.isBuilt || this.isExtensionDevelopmentHost) {
this.windowService.broadcast({
channel: EXTENSION_LOG_BROADCAST_CHANNEL,
payload: logEntry
}, this.environmentService.extensionDevelopmentPath /* target */);
}
}
private onError(err: any): void {
let errorMessage = toErrorMessage(err);
if (errorMessage === this.lastExtensionHostError) {
return; // prevent error spam
}
this.lastExtensionHostError = errorMessage;
this.messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage));
}
private onExit(code: any, signal: any, onProcessExit: any): void {
process.removeListener('exit', onProcessExit);
if (!this.terminating) {
// Unexpected termination
if (!this.isExtensionDevelopmentHost) {
this.messageService.show(Severity.Error, {
message: nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."),
actions: [this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL)]
});
console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
}
// Expected development extension termination: When the extension host goes down we also shutdown the window
else if (!this.isExtensionDevelopmentTestFromCli) {
this.windowService.getWindow().close();
}
// When CLI testing make sure to exit with proper exit code
else {
ipc.send('vscode:exit', code);
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册