diff --git a/IPython/kernel/kernelrestarter.py b/IPython/kernel/kernelrestarter.py index 66c377c..4d2dca8 100644 --- a/IPython/kernel/kernelrestarter.py +++ b/IPython/kernel/kernelrestarter.py @@ -41,22 +41,37 @@ class KernelRestarter(LoggingConfigurable): help="""Kernel heartbeat interval in seconds.""" ) + _pcallback = None + def __init__(self, **kwargs): super(KernelRestarter, self).__init__(**kwargs) def start(self): - self.pc = ioloop.PeriodicCallback(self.poll, self.time_to_dead, self.ioloop) - self.pc.start() + """Start the polling of the kernel.""" + if self._pcallback is None: + self._pcallback = ioloop.PeriodicCallback( + self._poll, 1000*self.time_to_dead, self.ioloop + ) + self._pcallback.start() + + def stop(self): + """Stop the kernel polling.""" + if self._pcallback is not None: + self._pcallback.stop() - def poll(self): + def clear(self): + """Clear the underlying PeriodicCallback.""" + self.stop() + if self._pcallback is not None: + self._pcallback = None + + def _poll(self): if not self.kernel_manager.is_alive(): self.stop() # This restart event should leave the connection file in place so # the ports are the same. Because this takes place below the # MappingKernelManager, the kernel_id will also remain the same. + self.log('KernelRestarter: restarting kernel') self.kernel_manager.restart_kernel(now=True); self.start() - def stop(self): - self.pc.stop() - self.pc = None diff --git a/IPython/kernel/multikernelmanager.py b/IPython/kernel/multikernelmanager.py index f5f343c..cd76389 100644 --- a/IPython/kernel/multikernelmanager.py +++ b/IPython/kernel/multikernelmanager.py @@ -23,12 +23,15 @@ import uuid import zmq from zmq.eventloop.zmqstream import ZMQStream +from zmq.eventloop import ioloop from IPython.config.configurable import LoggingConfigurable from IPython.utils.importstring import import_item from IPython.utils.traitlets import ( - Instance, Dict, Unicode, Any, DottedObjectName, + Instance, Dict, Unicode, Any, DottedObjectName, Bool ) +# from IPython.kernel.kernelrestarter import KernelRestarter + #----------------------------------------------------------------------------- # Classes #----------------------------------------------------------------------------- @@ -61,7 +64,7 @@ class MultiKernelManager(LoggingConfigurable): def _loop_default(self): return ioloop.IOLoop.instance() - autorestart = Bool(True, config=True, + autorestart = Bool(False, config=True, help="""Should we autorestart kernels that die.""" ) @@ -83,18 +86,29 @@ class MultiKernelManager(LoggingConfigurable): def __contains__(self, kernel_id): return kernel_id in self._kernels - def start_watching(self, kernel_id): - km = self.get_kernel(kernel_id): + def start_restarter(self, kernel_id): + km = self.get_kernel(kernel_id) if self.autorestart: - kr = KernelRestarter( - kernel_manager=km, loop=self.loop, - config=self.config, log=self.log - ) + kr = self._restarters.get(kernel_id, None) + if kr is None: + kr = KernelRestarter( + kernel_manager=km, loop=self.loop, + config=self.config, log=self.log + ) + self._restarters[kernel_id] = kr kr.start() - self._restarters[kernel_id] = kr - def stop_watching(self, kernel_id): + def stop_restarter(self, kernel_id): + if self.autorestart: + kr = self._restarters.get(kernel_id, None) + if kr is not None: + kr.stop() + def clear_restarter(self, kernel_id): + if self.autorestart: + kr = self._restarters.pop(kernel_id, None) + if kr is not None: + kr.stop() def start_kernel(self, **kwargs): """Start a new kernel. @@ -121,7 +135,7 @@ class MultiKernelManager(LoggingConfigurable): # start just the shell channel, needed for graceful restart km.start_channels(shell=True, iopub=False, stdin=False, hb=False) self._kernels[kernel_id] = km - self.start_watching(kernel_id) + self.start_restarter(kernel_id) return kernel_id def shutdown_kernel(self, kernel_id, now=False): @@ -135,11 +149,11 @@ class MultiKernelManager(LoggingConfigurable): Should the kernel be shutdown forcibly using a signal. """ k = self.get_kernel(kernel_id) - self.stop_watching(kernel_id) + self.stop_restarter(kernel_id) k.shutdown_kernel(now=now) k.shell_channel.stop() del self._kernels[kernel_id] - self.remove_watching(kernel_id) + self.clear_restarter(kernel_id) def shutdown_all(self, now=False): """Shutdown all kernels.""" @@ -178,9 +192,9 @@ class MultiKernelManager(LoggingConfigurable): The id of the kernel to interrupt. """ km = self.get_kernel(kernel_id) - self.stop_watching() + self.stop_restarter(kernel_id) km.restart_kernel() - self.start_watching() + self.start_restarter(kernel_id) def is_alive(self, kernel_id): """Is the kernel alive.