Skip to content

Commit

Permalink
fix uncaught error
Browse files Browse the repository at this point in the history
  • Loading branch information
seeM committed Nov 28, 2024
1 parent 065e6e7 commit 811cc2f
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 16 deletions.
27 changes: 12 additions & 15 deletions src/vs/workbench/services/runtimeSession/common/runtimeSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { DeferredPromise } from 'vs/base/common/async';
import { DeferredPromise, disposableTimeout } from 'vs/base/common/async';
import { Emitter } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
Expand Down Expand Up @@ -377,33 +377,30 @@ export class RuntimeSessionService extends Disposable implements IRuntimeSession

// We wait for `onDidEndSession()` rather than `RuntimeState.Exited`, because the former
// generates some Console output that must finish before starting up a new runtime:
let disposable: IDisposable | undefined;
const promise = new Promise<void>(resolve => {
disposable = sessionDisposables.add(session.onDidEndSession((exit) => {
const disposables = sessionDisposables.add(new DisposableStore());
const promise = new Promise<void>((resolve, reject) => {
disposables.add(session.onDidEndSession((exit) => {
disposables.dispose();
resolve();
disposable?.dispose();
}));
});

const timeout = new Promise<void>((_, reject) => {
setTimeout(() => {
disposable?.dispose();
disposables.add(disposableTimeout(() => {
disposables.dispose();
reject(new Error(`Timed out waiting for runtime ` +
`${formatLanguageRuntimeSession(session)} to finish exiting.`));
}, 5000);
}, 5000));
});

// Ask the runtime to shut down.
try {
await session.shutdown(exitReason);
} catch (err) {
disposable?.dispose();
throw err;
} catch (error) {
disposables.dispose();
throw error;
}

// Wait for the runtime onDidEndSession to resolve, or for the timeout to expire
// (whichever comes first)
await Promise.race([promise, timeout]);
await promise;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { formatLanguageRuntimeMetadata, ILanguageRuntimeMetadata, ILanguageRuntimeService, LanguageRuntimeSessionMode, RuntimeExitReason, RuntimeState } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService';
import { formatLanguageRuntimeMetadata, formatLanguageRuntimeSession, ILanguageRuntimeMetadata, ILanguageRuntimeService, LanguageRuntimeSessionMode, RuntimeExitReason, RuntimeState } from 'vs/workbench/services/languageRuntime/common/languageRuntimeService';
import { ILanguageRuntimeSession, IRuntimeSessionMetadata, IRuntimeSessionService, IRuntimeSessionWillStartEvent } from 'vs/workbench/services/runtimeSession/common/runtimeSessionService';
import { TestLanguageRuntimeSession, waitForRuntimeState } from 'vs/workbench/services/runtimeSession/test/common/testLanguageRuntimeSession';
import { createRuntimeServices, createTestLanguageRuntimeMetadata, startTestLanguageRuntimeSession } from 'vs/workbench/services/runtimeSession/test/common/testRuntimeSessionService';
Expand Down Expand Up @@ -653,6 +653,35 @@ suite('Positron - RuntimeSessionService', () => {
assert.equal(runtimeSessionService.foregroundSession, session1);
});

test(`select console to another runtime and first session never fires onDidEndSession`, async () => {
const session = await startConsole();
await waitForRuntimeState(session, RuntimeState.Ready);

// Stub onDidEndSession to never fire, causing the shutdown to time out.
sinon.stub(session, 'onDidEndSession').returns({ dispose: () => { } });

// Use a fake timer to avoid actually having to wait for the timeout.
const clock = sinon.useFakeTimers();
const promise = assert.rejects(selectRuntime(anotherRuntime), new Error(`Timed out waiting for runtime ` +
`${formatLanguageRuntimeSession(session)} to finish exiting.`));
await clock.tickAsync(10_000);
await promise;
});

test(`select console to another runtime encounters session.shutdown() error`, async () => {
const session = await startConsole();

// Stub session.shutdown() to throw an error.
const error = new Error('Session failed to shut down');
sinon.stub(session, 'shutdown').rejects(error);

// We also want to ensure that the timeout is not hit in this case but don't want to
// actually wait, so we use a fake timer.
const clock = sinon.useFakeTimers();
await assert.rejects(selectRuntime(anotherRuntime), error);
await clock.tickAsync(10_000);
});

function restartSession(sessionId: string) {
return runtimeSessionService.restartSession(sessionId, startReason);
}
Expand Down

0 comments on commit 811cc2f

Please sign in to comment.