Skip to content

Commit a455af3

Browse files
committed
Cleanup process finalization logic.
A follow up to fixes for #128.
1 parent a78e4d2 commit a455af3

File tree

5 files changed

+34
-26
lines changed

5 files changed

+34
-26
lines changed

tests/test_process.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,10 @@ def cancel_make_transport():
557557
except asyncio.CancelledError:
558558
pass
559559

560+
# Give the process handler some time to close itself
561+
yield from asyncio.sleep(0.3, loop=self.loop)
562+
gc.collect()
563+
560564
# ignore the log:
561565
# "Exception during subprocess creation, kill the subprocess"
562566
with tb.disable_logger():
@@ -575,6 +579,7 @@ def cancel_make_transport():
575579
except asyncio.CancelledError:
576580
pass
577581

582+
# Give the process handler some time to close itself
578583
yield from asyncio.sleep(0.3, loop=self.loop)
579584
gc.collect()
580585

uvloop/handles/process.pxd

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ cdef class UVProcess(UVHandle):
2020
char *uv_opt_file
2121
bytes __cwd
2222

23-
bint _kill_requested
24-
2523
cdef _init(self, Loop loop, list args, dict env, cwd,
2624
start_new_session,
2725
_stdin, _stdout, _stderr, pass_fds,

uvloop/handles/process.pyx

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ cdef class UVProcess(UVHandle):
1010
self._fds_to_close = set()
1111
self._preexec_fn = None
1212
self._restore_signals = True
13-
self._kill_requested = False
1413

1514
cdef _init(self, Loop loop, list args, dict env,
1615
cwd, start_new_session,
@@ -115,6 +114,14 @@ cdef class UVProcess(UVHandle):
115114
# after the process is finished.
116115
self._pid = (<uv.uv_process_t*>self._handle).pid
117116

117+
# Track the process handle (create a strong ref to it)
118+
# to guarantee that __dealloc__ doesn't happen in an
119+
# uncontrolled fashion. We want to wait until the process
120+
# exits and libuv calls __uvprocess_on_exit_callback,
121+
# which will call `UVProcess._close()`, which will, in turn,
122+
# untrack this handle.
123+
self._loop._track_process(self)
124+
118125
for fd in restore_inheritable:
119126
os_set_inheritable(fd, False)
120127

@@ -133,7 +140,7 @@ cdef class UVProcess(UVHandle):
133140
exc_name, exc_msg = errpipe_data.split(b':', 1)
134141
exc_name = exc_name.decode()
135142
exc_msg = exc_msg.decode()
136-
except:
143+
except Exception:
137144
self._close()
138145
raise subprocess_SubprocessError(
139146
'Bad exception data from child: {!r}'.format(
@@ -306,8 +313,6 @@ cdef class UVProcess(UVHandle):
306313
cdef _kill(self, int signum):
307314
cdef int err
308315
self._ensure_alive()
309-
if signum in {uv.SIGKILL, uv.SIGTERM}:
310-
self._kill_requested = True
311316
err = uv.uv_process_kill(<uv.uv_process_t*>self._handle, signum)
312317
if err < 0:
313318
raise convert_error(err)
@@ -323,6 +328,13 @@ cdef class UVProcess(UVHandle):
323328

324329
self._close()
325330

331+
cdef _close(self):
332+
try:
333+
if self._loop is not None:
334+
self._loop._untrack_process(self)
335+
finally:
336+
UVHandle._close(self)
337+
326338

327339
DEF _CALL_PIPE_DATA_RECEIVED = 0
328340
DEF _CALL_PIPE_CONNECTION_LOST = 1
@@ -360,6 +372,8 @@ cdef class UVProcessTransport(UVProcess):
360372
waiter.set_result(self._returncode)
361373
self._exit_waiters.clear()
362374

375+
self._close()
376+
363377
cdef _check_proc(self):
364378
if not self._is_alive() or self._returncode is not None:
365379
raise ProcessLookupError()
@@ -537,11 +551,6 @@ cdef class UVProcessTransport(UVProcess):
537551
else:
538552
self._pending_calls.append((_CALL_CONNECTION_LOST, None, None))
539553

540-
cdef _warn_unclosed(self):
541-
if self._kill_requested:
542-
return
543-
super()._warn_unclosed()
544-
545554
def __stdio_inited(self, waiter, stdio_fut):
546555
exc = stdio_fut.exception()
547556
if exc is not None:
@@ -556,21 +565,6 @@ cdef class UVProcessTransport(UVProcess):
556565
<method1_t>self._call_connection_made,
557566
self, waiter))
558567

559-
cdef _dealloc_impl(self):
560-
cdef int fix_needed
561-
562-
if UVLOOP_DEBUG:
563-
# Check when __dealloc__ will simply call uv.uv_close()
564-
# directly, thus *skipping* incrementing the debug counter;
565-
# we need to fix that.
566-
fix_needed = not self._closed and self._inited
567-
568-
UVProcess._dealloc_impl(self)
569-
570-
if UVLOOP_DEBUG and fix_needed and self._kill_requested:
571-
self._loop._debug_handles_closed.update([
572-
self.__class__.__name__])
573-
574568
@staticmethod
575569
cdef UVProcessTransport new(Loop loop, protocol, args, env,
576570
cwd, start_new_session,

uvloop/loop.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ cdef class Loop:
5858
set _servers
5959

6060
object _transports
61+
set _processes
6162
dict _fd_to_reader_fileobj
6263
dict _fd_to_writer_fileobj
6364

@@ -170,6 +171,9 @@ cdef class Loop:
170171
cdef _fileobj_to_fd(self, fileobj)
171172
cdef _ensure_fd_no_transport(self, fd)
172173

174+
cdef _track_process(self, UVProcess proc)
175+
cdef _untrack_process(self, UVProcess proc)
176+
173177
cdef _new_reader_future(self, sock)
174178
cdef _new_writer_future(self, sock)
175179
cdef _add_reader(self, fd, Handle handle)

uvloop/loop.pyx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ cdef class Loop:
101101
self._stopping = 0
102102

103103
self._transports = weakref_WeakValueDictionary()
104+
self._processes = set()
104105

105106
# Used to keep a reference (and hence keep the fileobj alive)
106107
# for as long as its registered by add_reader or add_writer.
@@ -613,6 +614,12 @@ cdef class Loop:
613614
cdef _track_transport(self, UVBaseTransport transport):
614615
self._transports[transport._fileno()] = transport
615616

617+
cdef _track_process(self, UVProcess proc):
618+
self._processes.add(proc)
619+
620+
cdef _untrack_process(self, UVProcess proc):
621+
self._processes.discard(proc)
622+
616623
cdef _fileobj_to_fd(self, fileobj):
617624
"""Return a file descriptor from a file object.
618625

0 commit comments

Comments
 (0)