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

Commit a84da85

Browse files
committed
make fork + exec non blocking on unix
1 parent d427e73 commit a84da85

File tree

4 files changed

+541
-96
lines changed

4 files changed

+541
-96
lines changed

asyncio/base_subprocess.py

+70-49
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,53 @@ def __init__(self, loop, protocol, args, shell,
3535
self._pipes[2] = None
3636

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

45-
self._pid = self._proc.pid
46-
self._extra['subprocess'] = self._proc
58+
self._pid = self._proc.pid
59+
self._extra['subprocess'] = self._proc
4760

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

5886
def __repr__(self):
5987
info = [self.__class__.__name__]
@@ -161,40 +189,33 @@ def kill(self):
161189

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

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

0 commit comments

Comments
 (0)