| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- """A basic in process kernel monitor with autorestarting.
- This watches a kernel's state using KernelManager.is_alive and auto
- restarts the kernel if it dies.
- """
- # Copyright (c) Jupyter Development Team.
- # Distributed under the terms of the Modified BSD License.
- import time
- import warnings
- from typing import Any
- from traitlets import Instance
- from ..restarter import KernelRestarter
- class IOLoopKernelRestarter(KernelRestarter):
- """Monitor and autorestart a kernel."""
- loop = Instance("tornado.ioloop.IOLoop")
- def _loop_default(self) -> Any:
- warnings.warn(
- "IOLoopKernelRestarter.loop is deprecated in jupyter-client 5.2",
- DeprecationWarning,
- stacklevel=4,
- )
- from tornado import ioloop
- return ioloop.IOLoop.current()
- _pcallback = None
- def start(self) -> None:
- """Start the polling of the kernel."""
- if self._pcallback is None:
- from tornado.ioloop import PeriodicCallback
- self._pcallback = PeriodicCallback(
- self.poll,
- 1000 * self.time_to_dead,
- )
- self._pcallback.start()
- def stop(self) -> None:
- """Stop the kernel polling."""
- if self._pcallback is not None:
- self._pcallback.stop()
- self._pcallback = None
- class AsyncIOLoopKernelRestarter(IOLoopKernelRestarter):
- """An async io loop kernel restarter."""
- async def poll(self) -> None: # type:ignore[override]
- """Poll the kernel."""
- if self.debug:
- self.log.debug("Polling kernel...")
- is_alive = await self.kernel_manager.is_alive()
- now = time.time()
- if not is_alive:
- self._last_dead = now
- if self._restarting:
- self._restart_count += 1
- else:
- self._restart_count = 1
- if self._restart_count > self.restart_limit:
- self.log.warning("AsyncIOLoopKernelRestarter: restart failed")
- self._fire_callbacks("dead")
- self._restarting = False
- self._restart_count = 0
- self.stop()
- else:
- newports = self.random_ports_until_alive and self._initial_startup
- self.log.info(
- "AsyncIOLoopKernelRestarter: restarting kernel (%i/%i), %s random ports",
- self._restart_count,
- self.restart_limit,
- "new" if newports else "keep",
- )
- self._fire_callbacks("restart")
- await self.kernel_manager.restart_kernel(now=True, newports=newports)
- self._restarting = True
- else:
- # Since `is_alive` only tests that the kernel process is alive, it does not
- # indicate that the kernel has successfully completed startup. To solve this
- # correctly, we would need to wait for a kernel info reply, but it is not
- # necessarily appropriate to start a kernel client + channels in the
- # restarter. Therefore, we use "has been alive continuously for X time" as a
- # heuristic for a stable start up.
- # See https://github.com/jupyter/jupyter_client/pull/717 for details.
- stable_start_time = self.stable_start_time
- if self.kernel_manager.provisioner:
- stable_start_time = self.kernel_manager.provisioner.get_stable_start_time(
- recommended=stable_start_time
- )
- if self._initial_startup and now - self._last_dead >= stable_start_time:
- self._initial_startup = False
- if self._restarting and now - self._last_dead >= stable_start_time:
- self.log.debug("AsyncIOLoopKernelRestarter: restart apparently succeeded")
- self._restarting = False
|