Skip to content

Commit 4f6621e

Browse files
committed
Close any open FDs if spawn fails (#186)
Addresses issue #185.
1 parent e0b5ea0 commit 4f6621e

File tree

3 files changed

+38
-28
lines changed

3 files changed

+38
-28
lines changed

uvloop/handles/handle.pyx

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ cdef class UVHandle:
2828
def __dealloc__(self):
2929
if UVLOOP_DEBUG:
3030
if self._loop is not None:
31-
self._loop._debug_handles_current.subtract([
32-
self.__class__.__name__])
31+
if self._inited:
32+
self._loop._debug_handles_current.subtract([
33+
self.__class__.__name__])
3334
else:
3435
# No "@cython.no_gc_clear" decorator on this UVHandle
3536
raise RuntimeError(
@@ -72,7 +73,7 @@ cdef class UVHandle:
7273
if self._handle == NULL:
7374
return
7475

75-
if UVLOOP_DEBUG:
76+
if UVLOOP_DEBUG and self._inited:
7677
self._loop._debug_uv_handles_freed += 1
7778

7879
PyMem_RawFree(self._handle)
@@ -99,16 +100,17 @@ cdef class UVHandle:
99100
if self._handle is not NULL:
100101
self._free()
101102

102-
self._closed = 1
103-
104-
if UVLOOP_DEBUG:
105-
name = self.__class__.__name__
106-
if self._inited:
107-
raise RuntimeError(
108-
'_abort_init: {}._inited is set'.format(name))
109-
if self._closed:
110-
raise RuntimeError(
111-
'_abort_init: {}._closed is set'.format(name))
103+
try:
104+
if UVLOOP_DEBUG:
105+
name = self.__class__.__name__
106+
if self._inited:
107+
raise RuntimeError(
108+
'_abort_init: {}._inited is set'.format(name))
109+
if self._closed:
110+
raise RuntimeError(
111+
'_abort_init: {}._closed is set'.format(name))
112+
finally:
113+
self._closed = 1
112114

113115
cdef inline _finish_init(self):
114116
self._inited = 1
@@ -117,7 +119,10 @@ cdef class UVHandle:
117119
if self._loop._debug:
118120
self._source_traceback = extract_stack()
119121
if UVLOOP_DEBUG:
122+
cls_name = self.__class__.__name__
120123
self._loop._debug_uv_handles_total += 1
124+
self._loop._debug_handles_total.update([cls_name])
125+
self._loop._debug_handles_current.update([cls_name])
121126

122127
cdef inline _start_init(self, Loop loop):
123128
if UVLOOP_DEBUG:
@@ -126,10 +131,6 @@ cdef class UVHandle:
126131
'{}._start_init can only be called once'.format(
127132
self.__class__.__name__))
128133

129-
cls_name = self.__class__.__name__
130-
loop._debug_handles_total.update([cls_name])
131-
loop._debug_handles_current.update([cls_name])
132-
133134
self._loop = loop
134135

135136
cdef inline bint _is_alive(self):

uvloop/handles/process.pyx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,16 @@ cdef class UVProcess(UVHandle):
7979

8080
if _PyImport_ReleaseLock() < 0:
8181
# See CPython/posixmodule.c for details
82-
self._abort_init()
82+
if err < 0:
83+
self._abort_init()
84+
else:
85+
self._close()
8386
raise RuntimeError('not holding the import lock')
8487

8588
if err < 0:
89+
if UVLOOP_DEBUG and uv.uv_is_active(self._handle):
90+
raise RuntimeError(
91+
'active uv_process_t handle after failed uv_spawn')
8692
self._abort_init()
8793
raise convert_error(err)
8894

@@ -109,6 +115,14 @@ cdef class UVProcess(UVHandle):
109115
# Might be already closed
110116
pass
111117

118+
fds_to_close = self._fds_to_close
119+
self._fds_to_close = None
120+
for fd in fds_to_close:
121+
os_close(fd)
122+
123+
for fd in restore_inheritable:
124+
os_set_inheritable(fd, False)
125+
112126
# asyncio caches the PID in BaseSubprocessTransport,
113127
# so that the transport knows what the PID was even
114128
# after the process is finished.
@@ -122,14 +136,6 @@ cdef class UVProcess(UVHandle):
122136
# untrack this handle.
123137
self._loop._track_process(self)
124138

125-
for fd in restore_inheritable:
126-
os_set_inheritable(fd, False)
127-
128-
fds_to_close = self._fds_to_close
129-
self._fds_to_close = None
130-
for fd in fds_to_close:
131-
os_close(fd)
132-
133139
if debug_flags & __PROCESS_DEBUG_SLEEP_AFTER_FORK:
134140
time_sleep(1)
135141

@@ -216,7 +222,7 @@ cdef class UVProcess(UVHandle):
216222

217223
for i in range(arr_len):
218224
el = arr[i]
219-
# NB: PyBytes_AsSptring doesn't copy the data;
225+
# NB: PyBytes_AsString doesn't copy the data;
220226
# we have to be careful when the "arr" is GCed,
221227
# and it shouldn't be ever mutated.
222228
ret[i] = PyBytes_AsString(el)
@@ -503,10 +509,12 @@ cdef class UVProcessTransport(UVProcess):
503509

504510
assert len(io) == 3
505511
for idx in range(3):
512+
iocnt = &self.iocnt[idx]
506513
if io[idx] is not None:
507-
iocnt = &self.iocnt[idx]
508514
iocnt.flags = uv.UV_INHERIT_FD
509515
iocnt.data.fd = io[idx]
516+
else:
517+
iocnt.flags = uv.UV_IGNORE
510518

511519
cdef _call_connection_made(self, waiter):
512520
try:

uvloop/includes/uv.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ cdef extern from "uv.h" nogil:
236236
int uv_cancel(uv_req_t* req)
237237

238238
# Generic handler functions
239+
int uv_is_active(const uv_handle_t* handle)
239240
void uv_close(uv_handle_t* handle, uv_close_cb close_cb)
240241
int uv_is_closing(const uv_handle_t* handle)
241242
int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd)

0 commit comments

Comments
 (0)