(
session: IKernelSession,
pendingExecutions: Promise<unknown>
)
| 558 | } |
| 559 | |
| 560 | private async interruptExecution( |
| 561 | session: IKernelSession, |
| 562 | pendingExecutions: Promise<unknown> |
| 563 | ): Promise<InterruptResult> { |
| 564 | const restarted = createDeferred<boolean>(); |
| 565 | const stopWatch = new StopWatch(); |
| 566 | const disposables: IDisposable[] = []; |
| 567 | // Listen to status change events so we can tell if we're restarting |
| 568 | const restartHandler = (e: KernelMessage.Status) => { |
| 569 | if (e === 'restarting' || e === 'autorestarting') { |
| 570 | // We restarted the kernel. |
| 571 | logger.warn('Kernel restarting during interrupt'); |
| 572 | |
| 573 | // Indicate we restarted the race below |
| 574 | restarted.resolve(true); |
| 575 | } |
| 576 | }; |
| 577 | const statusChangedHandler = (_: unknown, e: KernelMessage.Status) => restartHandler(e); |
| 578 | session.statusChanged.connect(statusChangedHandler); |
| 579 | disposables.push( |
| 580 | new Disposable(() => swallowExceptions(() => session.statusChanged.disconnect(statusChangedHandler))) |
| 581 | ); |
| 582 | |
| 583 | if (session && session.kernel) { |
| 584 | logger.info(`Interrupting kernel: ${session.kernel.name}`); |
| 585 | |
| 586 | // Start our interrupt. If it fails, indicate a restart |
| 587 | await raceTimeoutError( |
| 588 | this.kernelSettings.interruptTimeout, |
| 589 | new KernelInterruptTimeoutError(this.kernelConnectionMetadata), |
| 590 | session.kernel.interrupt() |
| 591 | ).catch((exc) => { |
| 592 | logger.warn(`Error during interrupt: ${exc}`); |
| 593 | restarted.resolve(true); |
| 594 | }); |
| 595 | } |
| 596 | |
| 597 | const promise = (async () => { |
| 598 | // Sometimes kernels can die during interrupt, so wait for the session to die (if not already dead and only when busy) |
| 599 | // I.e. we do not want to handle cases where kernel dies when not busy. |
| 600 | const timedOutPromiseDueToDeadKernel = createDeferred<InterruptResult>(); |
| 601 | if (this.status === 'busy') { |
| 602 | this.onDisposed(() => timedOutPromiseDueToDeadKernel.resolve(InterruptResult.Dead), this, disposables); |
| 603 | this.onStatusChanged( |
| 604 | () => |
| 605 | this.status === 'dead' |
| 606 | ? timedOutPromiseDueToDeadKernel.resolve(InterruptResult.Dead) |
| 607 | : undefined, |
| 608 | this, |
| 609 | disposables |
| 610 | ); |
| 611 | } |
| 612 | try { |
| 613 | // Wait for all of the pending cells to finish or the timeout to fire |
| 614 | return await raceTimeout( |
| 615 | this.kernelSettings.interruptTimeout, |
| 616 | InterruptResult.TimedOut, |
| 617 | pendingExecutions.then(() => InterruptResult.Success), |
nothing calls this directly
no test coverage detected