popen_forkserver.py 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import io
  2. import os
  3. from .context import reduction, set_spawning_popen
  4. if not reduction.HAVE_SEND_HANDLE:
  5. raise ImportError('No support for sending fds between processes')
  6. from . import forkserver
  7. from . import popen_fork
  8. from . import spawn
  9. from . import util
  10. __all__ = ['Popen']
  11. #
  12. # Wrapper for an fd used while launching a process
  13. #
  14. class _DupFd(object):
  15. def __init__(self, ind):
  16. self.ind = ind
  17. def detach(self):
  18. return forkserver.get_inherited_fds()[self.ind]
  19. #
  20. # Start child process using a server process
  21. #
  22. class Popen(popen_fork.Popen):
  23. method = 'forkserver'
  24. DupFd = _DupFd
  25. def __init__(self, process_obj):
  26. self._fds = []
  27. super().__init__(process_obj)
  28. def duplicate_for_child(self, fd):
  29. self._fds.append(fd)
  30. return len(self._fds) - 1
  31. def _launch(self, process_obj):
  32. prep_data = spawn.get_preparation_data(process_obj._name)
  33. buf = io.BytesIO()
  34. set_spawning_popen(self)
  35. try:
  36. reduction.dump(prep_data, buf)
  37. reduction.dump(process_obj, buf)
  38. finally:
  39. set_spawning_popen(None)
  40. self.sentinel, w = forkserver.connect_to_new_process(self._fds)
  41. # Keep a duplicate of the data pipe's write end as a sentinel of the
  42. # parent process used by the child process.
  43. _parent_w = os.dup(w)
  44. self.finalizer = util.Finalize(self, util.close_fds,
  45. (_parent_w, self.sentinel))
  46. with open(w, 'wb', closefd=True) as f:
  47. f.write(buf.getbuffer())
  48. self.pid = forkserver.read_signed(self.sentinel)
  49. def poll(self, flag=os.WNOHANG):
  50. if self.returncode is None:
  51. from multiprocessing.connection import wait
  52. timeout = 0 if flag == os.WNOHANG else None
  53. if not wait([self.sentinel], timeout):
  54. return None
  55. try:
  56. self.returncode = forkserver.read_signed(self.sentinel)
  57. except (OSError, EOFError):
  58. # This should not happen usually, but perhaps the forkserver
  59. # process itself got killed
  60. self.returncode = 255
  61. return self.returncode