Message200598
> 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... |
|
Date |
User |
Action |
Args |
2013-10-20 17:36:37 | neologix | set | recipients:
+ neologix, gvanrossum, db3l, ncoghlan, pitrou, larry, skrah, python-dev, David.Edelsohn |
2013-10-20 17:36:37 | neologix | link | issue19293 messages |
2013-10-20 17:36:37 | neologix | create | |
|