Charles-François Natali added the comment: > I guess we'll have to write platform-dependent code and make this an > optional feature. (Essentially, on platforms like AIX, for a > write-pipe, connection_lost() won't be called unless you try to write > some more bytes to it.) > > I do believe that so far this problem only occurs on AIX so I am > tempted to make it an explicit test for AIX -- if it's AIX, don't > register the _read_ready handler. We'll also have to skip or adjust > one or two tests that will fail if this feature is missing.
Hmm... Not calling connection_lost() when the read-end of the pipe is closed is OK, since there's no portable way to detect this on e.g. AIX, but AFAICT the current process-termination logic is buggy: When SIGCHLD is received, _sig_chld() is executed: def _sig_chld(self): [...] transp = self._subprocesses.get(pid) if transp is not None: transp._process_exited(returncode) Then, here's _process_exited(): def _process_exited(self, returncode): assert returncode is not None, returncode assert self._returncode is None, self._returncode self._returncode = returncode self._loop._subprocess_closed(self) self._call(self._protocol.process_exited) self._try_finish() And here's _try_finish(): def _try_finish(self): assert not self._finished if self._returncode is None: return if all(p is not None and p.disconnected for p in self._pipes.values()): self._finished = True self._loop.call_soon(self._call_connection_lost, None) Thus, _UnixSubprocessTransport protocol's connection_lost is only called if the all() expression is true: and it's true only if all the subprocess pipes have been disconnected (or if we didn't setup any pipe). Unfortunately, this might very well never happen: imagine that the subprocess forks a process: this grand-child process inherits the child process's pipes, so when the child process exits, we won't receive any notification, since this grand-child process still has open FDs pointing to the original child's stdin/stdout/stderr. The following simple test will hang until the background 'sleep' exits: """ diff -r 47618b00405b Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/test/test_asyncio/test_events.py Sun Oct 20 19:32:37 2013 +0200 @@ -1059,6 +1059,23 @@ @unittest.skipIf(sys.platform == 'win32', "Don't support subprocess for Windows yet") + def test_subprocess_inherit_fds(self): + proto = None + + @tasks.coroutine + def connect(): + nonlocal proto + transp, proto = yield from self.loop.subprocess_shell( + functools.partial(MySubprocessProtocol, self.loop), + 'sleep 60 &') + self.assertIsInstance(proto, MySubprocessProtocol) + + self.loop.run_until_complete(connect()) + self.loop.run_until_complete(proto.completed) + self.assertEqual(0, proto.returncode) + + @unittest.skipIf(sys.platform == 'win32', + "Don't support subprocess for Windows yet") def test_subprocess_close_after_finish(self): proto = None transp = None """ If waitpid() returns a process's PID, then the process is done, there's no reason to further wait for pipe's disconnection: they can be used as a hint that the process terminated, but there's definitely not required... ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue19293> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com