Skip to content

Commit f4c1ac3

Browse files
Windows raw kernel launch failure fix (#11603)
1 parent 71d1793 commit f4c1ac3

File tree

6 files changed

+68
-3
lines changed

6 files changed

+68
-3
lines changed

news/2 Fixes/11574.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix intermittent launch failure with raw kernels on windows.

package-lock.json

Lines changed: 40 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,6 +2929,7 @@
29292929
"@koa/cors": "^3.0.0",
29302930
"@loadable/component": "^5.12.0",
29312931
"@nteract/messaging": "^7.0.0",
2932+
"@types/tcp-port-used": "^1.0.0",
29322933
"ansi-regex": "^4.1.0",
29332934
"arch": "^2.1.0",
29342935
"azure-storage": "^2.10.3",
@@ -2972,6 +2973,7 @@
29722973
"strip-ansi": "^5.2.0",
29732974
"sudo-prompt": "^8.2.0",
29742975
"svg-to-pdfkit": "^0.1.8",
2976+
"tcp-port-used": "^1.0.1",
29752977
"tmp": "^0.0.29",
29762978
"tree-kill": "^1.2.2",
29772979
"typescript-char": "^0.0.0",

src/client/datascience/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ export enum Telemetry {
313313
RawKernelSessionStartSuccess = 'DS_INTERNAL.RAWKERNEL_SESSION_START_SUCCESS',
314314
RawKernelSessionStartUserCancel = 'DS_INTERNAL.RAWKERNEL_SESSION_START_USER_CANCEL',
315315
RawKernelSessionStartTimeout = 'DS_INTERNAL.RAWKERNEL_SESSION_START_TIMEOUT',
316-
RawKernelSessionStartException = 'DS_INTERNAL.RAWKERNEL_SESSION_START_EXCEPTION'
316+
RawKernelSessionStartException = 'DS_INTERNAL.RAWKERNEL_SESSION_START_EXCEPTION',
317+
RawKernelProcessLaunch = 'DS_INTERNAL.RAWKERNEL_PROCESS_LAUNCH'
317318
}
318319

319320
export enum NativeKeyboardCommandTelemetry {

src/client/datascience/kernel-launcher/kernelProcess.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
'use strict';
44

55
import { ChildProcess } from 'child_process';
6+
import * as tcpPortUsed from 'tcp-port-used';
67
import { Event, EventEmitter } from 'vscode';
78
import { PYTHON_LANGUAGE } from '../../common/constants';
89
import { traceError, traceInfo, traceWarning } from '../../common/logger';
@@ -11,6 +12,8 @@ import { IProcessServiceFactory, ObservableExecutionResult } from '../../common/
1112
import { Resource } from '../../common/types';
1213
import { noop, swallowExceptions } from '../../common/utils/misc';
1314
import { PythonInterpreter } from '../../interpreter/contracts';
15+
import { captureTelemetry } from '../../telemetry';
16+
import { Telemetry } from '../constants';
1417
import { IJupyterKernelSpec } from '../types';
1518
import { findIndexOfConnectionFile } from './kernelFinder';
1619
import { PythonKernelLauncherDaemon } from './kernelLauncherDaemon';
@@ -61,6 +64,8 @@ export class KernelProcess implements IKernelProcess {
6164
await this.kernelDaemon?.interrupt();
6265
}
6366
}
67+
68+
@captureTelemetry(Telemetry.RawKernelProcessLaunch, undefined, true)
6469
public async launch(): Promise<void> {
6570
if (this.launchedOnce) {
6671
throw new Error('Kernel has already been launched.');
@@ -103,6 +108,9 @@ export class KernelProcess implements IKernelProcess {
103108
}
104109
}
105110
);
111+
112+
// Don't return until our heartbeat channel is open for connections
113+
return this.waitForHeartbeat();
106114
}
107115

108116
public async dispose(): Promise<void> {
@@ -119,6 +127,20 @@ export class KernelProcess implements IKernelProcess {
119127
swallowExceptions(() => this.connectionFile?.dispose());
120128
}
121129

130+
// Make sure that the heartbeat channel is open for connections
131+
private async waitForHeartbeat() {
132+
try {
133+
// Wait until the port is open for connection
134+
// First parameter is wait between retries, second parameter is total wait before error
135+
await tcpPortUsed.waitUntilUsed(this.connection.hb_port, 200, 30_000);
136+
} catch (error) {
137+
// Make sure to dispose if we never get a heartbeat
138+
this.dispose().ignoreErrors();
139+
traceError('Timed out waiting to get a heartbeat from kernel process.');
140+
throw new Error('Timed out waiting to get a heartbeat from kernel process.');
141+
}
142+
}
143+
122144
private async createAndUpdateConnectionFile() {
123145
this.connectionFile = await this.file.createTemporaryFile('.json');
124146
await this.file.writeFile(this.connectionFile.filePath, JSON.stringify(this._connection), {

src/client/telemetry/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,6 +2098,7 @@ export interface IEventNamePropertyMapping {
20982098
// Raw kernel timing events
20992099
[Telemetry.RawKernelSessionConnect]: never | undefined;
21002100
[Telemetry.RawKernelStartRawSession]: never | undefined;
2101+
[Telemetry.RawKernelProcessLaunch]: never | undefined;
21012102

21022103
// Raw kernel single events
21032104
[Telemetry.RawKernelSessionStartSuccess]: never | undefined;

0 commit comments

Comments
 (0)