##// END OF EJS Templates
Kernel subprocess parent polling is now implemented for Windows.
epatters -
Show More
@@ -11,11 +11,16 b' Things to do:'
11 11 * Implement event loop and poll version.
12 12 """
13 13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
14 18 # Standard library imports.
15 19 import __builtin__
16 20 from code import CommandCompiler
17 21 import os
18 22 import sys
23 from threading import Thread
19 24 import time
20 25 import traceback
21 26
@@ -27,6 +32,9 b' from IPython.external.argparse import ArgumentParser'
27 32 from session import Session, Message, extract_header
28 33 from completer import KernelCompleter
29 34
35 #-----------------------------------------------------------------------------
36 # Kernel and stream classes
37 #-----------------------------------------------------------------------------
30 38
31 39 class InStream(object):
32 40 """ A file like object that reads from a 0MQ XREQ socket."""
@@ -210,7 +218,6 b' class Kernel(object):'
210 218 self.history = []
211 219 self.compiler = CommandCompiler()
212 220 self.completer = KernelCompleter(self.user_ns)
213 self.poll_ppid = False
214 221
215 222 # Build dict of handlers for message types
216 223 msg_types = [ 'execute_request', 'complete_request',
@@ -325,10 +332,6 b' class Kernel(object):'
325 332
326 333 def start(self):
327 334 while True:
328 if self.poll_ppid and os.getppid() == 1:
329 print>>sys.__stderr__, "KILLED KERNEL. No parent process."
330 os._exit(1)
331
332 335 ident = self.reply_socket.recv()
333 336 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
334 337 msg = self.reply_socket.recv_json()
@@ -341,6 +344,34 b' class Kernel(object):'
341 344 else:
342 345 handler(ident, omsg)
343 346
347 #-----------------------------------------------------------------------------
348 # Kernel main and launch functions
349 #-----------------------------------------------------------------------------
350
351 class UnixPoller(Thread):
352
353 def __init__(self):
354 super(UnixPoller, self).__init__()
355 self.daemon = True
356
357 def run(self):
358 while True:
359 if os.getppid() == 1:
360 os._exit(1)
361 time.sleep(5.0)
362
363 class WindowsPoller(Thread):
364
365 def __init__(self, handle):
366 super(WindowsPoller, self).__init__()
367 self.daemon = True
368 self.handle = handle
369
370 def run(self):
371 from _subprocess import WaitForSingleObject, WAIT_OBJECT_0, INFINITE
372 result = WaitForSingleObject(self.handle, INFINITE)
373 if result == WAIT_OBJECT_0:
374 os._exit(1)
344 375
345 376 def bind_port(socket, ip, port):
346 377 """ Binds the specified ZMQ socket. If the port is less than zero, a random
@@ -367,8 +398,13 b' def main():'
367 398 help='set the PUB channel port [default: random]')
368 399 parser.add_argument('--req', type=int, metavar='PORT', default=0,
369 400 help='set the REQ channel port [default: random]')
370 parser.add_argument('--require-parent', action='store_true',
371 help='ensure that this process dies with its parent')
401 if sys.platform == 'win32':
402 parser.add_argument('--parent', type=int, metavar='HANDLE',
403 default=0, help='kill this process if the process '
404 'with HANDLE dies')
405 else:
406 parser.add_argument('--parent', action='store_true',
407 help='kill this process if its parent dies')
372 408 namespace = parser.parse_args()
373 409
374 410 # Create a context, a session, and the kernel sockets.
@@ -398,15 +434,18 b' def main():'
398 434 kernel = Kernel(session, reply_socket, pub_socket)
399 435
400 436 # Configure this kernel/process to die on parent termination, if necessary.
401 if namespace.require_parent:
437 if namespace.parent:
402 438 if sys.platform == 'linux2':
403 439 import ctypes, ctypes.util, signal
404 440 PR_SET_PDEATHSIG = 1
405 441 libc = ctypes.CDLL(ctypes.util.find_library('c'))
406 442 libc.prctl(PR_SET_PDEATHSIG, signal.SIGKILL)
407
408 elif sys.platform != 'win32':
409 kernel.poll_ppid = True
443 elif sys.platform == 'win32':
444 poller = WindowsPoller(namespace.parent)
445 poller.start()
446 else:
447 poller = UnixPoller()
448 poller.start()
410 449
411 450 # Start the kernel mainloop.
412 451 kernel.start()
@@ -430,7 +469,7 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):'
430 469 If set, the kernel process is guaranteed to survive if this process
431 470 dies. If not set, an effort is made to ensure that the kernel is killed
432 471 when this process dies. Note that in this case it is still good practice
433 to attempt to kill kernels manually before exiting.
472 to kill kernels manually before exiting.
434 473
435 474 Returns
436 475 -------
@@ -463,15 +502,22 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False):'
463 502 command = 'from IPython.zmq.kernel import main; main()'
464 503 arguments = [ sys.executable, '-c', command, '--xrep', str(xrep_port),
465 504 '--pub', str(pub_port), '--req', str(req_port) ]
466
467 505 if independent:
468 506 if sys.platform == 'win32':
469 507 proc = Popen(['start', '/b'] + arguments, shell=True)
470 508 else:
471 509 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
472
473 510 else:
474 proc = Popen(arguments + ['--require-parent'])
511 if sys.platform == 'win32':
512 from _subprocess import DuplicateHandle, GetCurrentProcess, \
513 DUPLICATE_SAME_ACCESS
514 pid = GetCurrentProcess()
515 handle = DuplicateHandle(pid, pid, pid, 0,
516 True, # Inheritable by new processes.
517 DUPLICATE_SAME_ACCESS)
518 proc = Popen(arguments + ['--parent', str(int(handle))])
519 else:
520 proc = Popen(arguments + ['--parent'])
475 521
476 522 return proc, xrep_port, pub_port, req_port
477 523
General Comments 0
You need to be logged in to leave comments. Login now