Skip to content
This repository was archived by the owner on Nov 23, 2017. It is now read-only.

Commit 8fef3fe

Browse files
committed
make fork + exec non blocking on unix
1 parent 8e8416f commit 8fef3fe

File tree

4 files changed

+541
-96
lines changed

4 files changed

+541
-96
lines changed

asyncio/base_subprocess.py

Lines changed: 70 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,53 @@ def __init__(self, loop, protocol, args, shell,
3434
self._pipes[2] = None
3535

3636
# Create the child process: set the _proc attribute
37+
self._loop.create_task(self._create_child(
38+
waiter, args=args, shell=shell, stdin=stdin, stdout=stdout,
39+
stderr=stderr, bufsize=bufsize, start_kwargs=kwargs))
40+
41+
@coroutine
42+
def _create_child(self, waiter, args, shell, stdin, stdout, stderr,
43+
bufsize, start_kwargs):
3744
try:
38-
self._start(args=args, shell=shell, stdin=stdin, stdout=stdout,
39-
stderr=stderr, bufsize=bufsize, **kwargs)
40-
except:
41-
self.close()
42-
raise
45+
try:
46+
start = self._start(args=args, shell=shell, stdin=stdin,
47+
stdout=stdout, stderr=stderr,
48+
bufsize=bufsize, **start_kwargs)
49+
50+
if start is not None:
51+
# _start is not required to be a coroutine
52+
yield from start
53+
except:
54+
self.close()
55+
raise
4356

44-
self._pid = self._proc.pid
45-
self._extra['subprocess'] = self._proc
57+
self._pid = self._proc.pid
58+
self._extra['subprocess'] = self._proc
4659

47-
if self._loop.get_debug():
48-
if isinstance(args, (bytes, str)):
49-
program = args
60+
if self._loop.get_debug():
61+
if isinstance(args, (bytes, str)):
62+
program = args
63+
else:
64+
program = args[0]
65+
logger.debug('process %r created: pid %s', program, self._pid)
66+
67+
if self._closed:
68+
# transport.close() may have been called concurrently, for
69+
# instance if _make_subprocess_transport() has been cancelled.
70+
if self._proc.stdin:
71+
self._proc.stdin.close()
72+
if self._proc.stdout:
73+
self._proc.stdout.close()
74+
if self._proc.stderr:
75+
self._proc.stderr.close()
5076
else:
51-
program = args[0]
52-
logger.debug('process %r created: pid %s',
53-
program, self._pid)
54-
55-
self._loop.create_task(self._connect_pipes(waiter))
77+
yield from self._connect_pipes(waiter)
78+
except Exception as exc:
79+
if waiter is not None and not waiter.cancelled():
80+
waiter.set_exception(exc)
81+
else:
82+
if waiter is not None and not waiter.cancelled():
83+
waiter.set_result(None)
5684

5785
def __repr__(self):
5886
info = [self.__class__.__name__]
@@ -160,40 +188,33 @@ def kill(self):
160188

161189
@coroutine
162190
def _connect_pipes(self, waiter):
163-
try:
164-
proc = self._proc
165-
loop = self._loop
166-
167-
if proc.stdin is not None:
168-
_, pipe = yield from loop.connect_write_pipe(
169-
lambda: WriteSubprocessPipeProto(self, 0),
170-
proc.stdin)
171-
self._pipes[0] = pipe
172-
173-
if proc.stdout is not None:
174-
_, pipe = yield from loop.connect_read_pipe(
175-
lambda: ReadSubprocessPipeProto(self, 1),
176-
proc.stdout)
177-
self._pipes[1] = pipe
178-
179-
if proc.stderr is not None:
180-
_, pipe = yield from loop.connect_read_pipe(
181-
lambda: ReadSubprocessPipeProto(self, 2),
182-
proc.stderr)
183-
self._pipes[2] = pipe
184-
185-
assert self._pending_calls is not None
186-
187-
loop.call_soon(self._protocol.connection_made, self)
188-
for callback, data in self._pending_calls:
189-
loop.call_soon(callback, *data)
190-
self._pending_calls = None
191-
except Exception as exc:
192-
if waiter is not None and not waiter.cancelled():
193-
waiter.set_exception(exc)
194-
else:
195-
if waiter is not None and not waiter.cancelled():
196-
waiter.set_result(None)
191+
proc = self._proc
192+
loop = self._loop
193+
194+
if proc.stdin is not None:
195+
_, pipe = yield from loop.connect_write_pipe(
196+
lambda: WriteSubprocessPipeProto(self, 0),
197+
proc.stdin)
198+
self._pipes[0] = pipe
199+
200+
if proc.stdout is not None:
201+
_, pipe = yield from loop.connect_read_pipe(
202+
lambda: ReadSubprocessPipeProto(self, 1),
203+
proc.stdout)
204+
self._pipes[1] = pipe
205+
206+
if proc.stderr is not None:
207+
_, pipe = yield from loop.connect_read_pipe(
208+
lambda: ReadSubprocessPipeProto(self, 2),
209+
proc.stderr)
210+
self._pipes[2] = pipe
211+
212+
assert self._pending_calls is not None
213+
214+
loop.call_soon(self._protocol.connection_made, self)
215+
for callback, data in self._pending_calls:
216+
loop.call_soon(callback, *data)
217+
self._pending_calls = None
197218

198219
def _call(self, cb, *data):
199220
if self._pending_calls is not None:

0 commit comments

Comments
 (0)