I'm not fond of the way reduction.DupHandle() expects the receiving process to steal the duplicated handle. I'd prefer using the resource_sharer module, like reduction.DupFd() does in POSIX. Except spawning is a special case, for which reduction.DupHandle() can take advantage of the duplicate_for_child() method of the popen_spawn_win32.Popen instance.
With the resource sharer, the handle still needs to be duplicated in the sending process. But an important difference is the resource_sharer.stop() method, which at least allows closing any handles that no longer need to be shared.
---
Proposed Changes (untested)
resource_sharer.py:
class DupHandle(object):
'''Wrapper for a handle that can be used at any time.'''
def __init__(self, handle):
dh = reduction.duplicate(handle)
def send(conn, pid):
reduction.send_handle(conn, dh, pid)
def close():
_winapi.CloseHandle(dh)
self._id = _resource_sharer.register(send, close)
def detach(self):
'''Get the handle. This should only be called once.'''
with _resource_sharer.get_connection(self._id) as conn:
return reduction.recv_handle(conn)
reduction.py:
def send_handle(conn, handle, destination_pid):
'''Send a handle over a local connection.'''
proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False,
destination_pid)
try:
dh = duplicate(handle, proc)
conn.send(dh)
finally:
_winapi.CloseHandle(proc)
def recv_handle(conn):
'''Receive a handle over a local connection.'''
return conn.recv()
class _DupHandle:
def __init__(self, handle):
self.handle = handle
def detach(self):
return self.handle
def DupHandle(handle):
'''Return a wrapper for a handle.'''
popen_obj = context.get_spawning_popen()
if popen_obj is not None:
return _DupHandle(popen_obj.duplicate_for_child(handle))
from . import resource_sharer
return resource_sharer.DupHandle(handle)
connection.py:
def reduce_pipe_connection(conn):
dh = reduction.DupHandle(conn.fileno())
return rebuild_pipe_connection, (dh, conn.readable, conn.writable)
def rebuild_pipe_connection(dh, readable, writable):
return PipeConnection(dh.detach(), readable, writable)
reduction.register(PipeConnection, reduce_pipe_connection)
|