This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author neologix
Recipients neologix
Date 2013-10-21.10:15:57
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1382350557.5.0.977227670741.issue19326@psf.upfronthosting.co.za>
In-reply-to
Content
(This is a spinoff from http://bugs.python.org/issue19293#msg200598)

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...
History
Date User Action Args
2013-10-21 10:15:57neologixsetrecipients: + neologix
2013-10-21 10:15:57neologixsetmessageid: <1382350557.5.0.977227670741.issue19326@psf.upfronthosting.co.za>
2013-10-21 10:15:57neologixlinkissue19326 messages
2013-10-21 10:15:57neologixcreate