Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions extensions/positron-supervisor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@
"default": 30,
"description": "%configuration.connectionTimeout.description%"
},
"kernelSupervisor.startupTimeout": {
"scope": "window",
"type": "number",
"default": 10,
"minimum": 1,
"description": "%configuration.startupTimeout.description%"
},
"kernelSupervisor.logLevel": {
"scope": "window",
"type": "string",
Expand Down
1 change: 1 addition & 0 deletions extensions/positron-supervisor/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"configuration.shutdownTimeout.indefinitely.description": "Leave kernels running indefinitely",
"configuration.showTerminal.description": "Show the host terminal for the Positron kernel supervisor",
"configuration.connectionTimeout.description": "Timeout in seconds for connecting to the kernel's sockets",
"configuration.startupTimeout.description": "Timeout in seconds for starting the kernel supervisor",
"configuration.attachOnStartup.description": "Run <f5> before starting up Jupyter kernel (when supported)",
"configuration.sleepOnStartup.description": "Sleep for n seconds before starting up Jupyter kernel (when supported)",
"configuration.runInShell.description": "Run kernels in a login shell (on Unix-like systems)",
Expand Down
54 changes: 33 additions & 21 deletions extensions/positron-supervisor/src/KallichoreAdapterApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ export class KCApi implements PositronSupervisorApi {
// Consult configuration to see if we should show this terminal
const config = vscode.workspace.getConfiguration('kernelSupervisor');
const showTerminal = config.get<boolean>('showTerminal', false);
const startupTimeout = config.get<number>('startupTimeout', 10) * 1000;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to log these config settings to the output pane?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! Done in 96ec61f.


// Create a temporary file with a random name to use for logs
const logFile = path.join(os.tmpdir(), `kallichore-${sessionId}.log`);
Expand Down Expand Up @@ -367,25 +368,11 @@ export class KCApi implements PositronSupervisorApi {
}
}

// Start the server in a new terminal
this.log(`Starting Kallichore server ${shellPath} with connection file ${connectionFile}`);
const terminal = vscode.window.createTerminal({
name: 'Kallichore',
shellPath: wrapperPath,
shellArgs,
message: `*** Kallichore Server (${shellPath}) ***`,
hideFromUser: !showTerminal,
isTransient: false
} satisfies vscode.TerminalOptions);

// Flag to track if the terminal exited before the start barrier opened
let exited = false;

// Listen for the terminal to close. If it closes unexpectedly before
// the start barrier opens, provide some feedback.
const closeListener = vscode.window.onDidCloseTerminal(async (closedTerminal) => {
// Ignore closed terminals that aren't the one we started
if (closedTerminal !== terminal) {
if (closedTerminal !== this._terminal) {
return;
}

Expand All @@ -405,8 +392,8 @@ export class KCApi implements PositronSupervisorApi {

// Read the contents of the output file and log it
const contents = fs.readFileSync(outFile, 'utf8');
if (terminal.exitStatus && terminal.exitStatus.code) {
this.log(`Supervisor terminal closed with exit code ${terminal.exitStatus.code}; output:\n${contents}`);
if (this._terminal.exitStatus && this._terminal.exitStatus.code) {
this.log(`Supervisor terminal closed with exit code ${this._terminal.exitStatus.code}; output:\n${contents}`);
} else {
this.log(`Supervisor terminal closed unexpectedly; output:\n${contents}`);
}
Expand All @@ -424,6 +411,21 @@ export class KCApi implements PositronSupervisorApi {
}
});


// Start the server in a new terminal
this.log(`Starting Kallichore server ${shellPath} with connection file ${connectionFile}`);
this._terminal = vscode.window.createTerminal({
name: 'Kallichore',
shellPath: wrapperPath,
shellArgs,
message: `*** Kallichore Server (${shellPath}) ***`,
hideFromUser: !showTerminal,
isTransient: false
} satisfies vscode.TerminalOptions);

// Flag to track if the terminal exited before the start barrier opened
let exited = false;

// Ensure this listener is disposed when the API is disposed
this._disposables.push(closeListener);

Expand All @@ -437,7 +439,7 @@ export class KCApi implements PositronSupervisorApi {
}));

// Wait for the terminal to start and get the PID
let processId = await terminal.processId;
let processId = await this._terminal.processId;

// Wait for the connection file to be written by the server
let connectionData: KallichoreServerState | undefined = undefined;
Expand Down Expand Up @@ -481,6 +483,16 @@ export class KCApi implements PositronSupervisorApi {
this.log(`Error reading connection file (attempt ${retry}): ${err}`);
}

// Every 10 retries, check to see if the server process has exited.
if (!exited && processId && retry % 10 === 0) {
try {
process.kill(processId, 0);
} catch (err) {
this.log(`Kallichore server PID ${processId} is not running`);
exited = true;
}
}

// Has the terminal exited? if it has, there's no point in continuing to retry.
if (exited) {
let message = `The supervisor process exited unexpectedly during startup`;
Expand All @@ -497,7 +509,7 @@ export class KCApi implements PositronSupervisorApi {
}

const elapsed = Date.now() - startTime;
if (elapsed > 10000) {
if (elapsed > startupTimeout) {
let message = `Connection file was not created after ${elapsed}ms`;

// Include any output from the server process to help diagnose the problem
Expand Down Expand Up @@ -574,7 +586,7 @@ export class KCApi implements PositronSupervisorApi {
// trying up to 10 seconds from the time we got a process ID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few places where "10 seconds" is referenced, including an error message Timed out waiting for connection file to be ` + `created at ${connectionFile} after 10 seconds Shall we change this to "startupTimeout seconds"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh thanks, I should have fixed those up! Addressed in 96ec61f.

// established.
if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') {
if (elapsed < 10000) {
if (elapsed < startupTimeout) {
// Log every few attempts. We don't want to overwhelm
// the logs, and it's normal for us to encounter a few
// connection refusals before the server is ready.
Expand Down Expand Up @@ -609,7 +621,7 @@ export class KCApi implements PositronSupervisorApi {
// If the request times out, go ahead and try again as long as
// it hasn't been more than 10 seconds since we started. This
// can happen if the server is slow to start.
if (err.code === 'ETIMEDOUT' && elapsed < 10000) {
if (err.code === 'ETIMEDOUT' && elapsed < startupTimeout) {
this.log(`Request for server status timed out; retrying (attempt ${retry + 1}, ${elapsed}ms)`);
continue;
}
Expand Down