Skip to content

Commit

Permalink
Close any open FDs if spawn fails (#186)
Browse files Browse the repository at this point in the history
Addresses issue #185.
  • Loading branch information
1st1 committed Aug 2, 2018
1 parent e0b5ea0 commit 4f6621e
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 28 deletions.
35 changes: 18 additions & 17 deletions uvloop/handles/handle.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ cdef class UVHandle:
def __dealloc__(self):
if UVLOOP_DEBUG:
if self._loop is not None:
self._loop._debug_handles_current.subtract([
self.__class__.__name__])
if self._inited:
self._loop._debug_handles_current.subtract([
self.__class__.__name__])
else:
# No "@cython.no_gc_clear" decorator on this UVHandle
raise RuntimeError(
Expand Down Expand Up @@ -72,7 +73,7 @@ cdef class UVHandle:
if self._handle == NULL:
return

if UVLOOP_DEBUG:
if UVLOOP_DEBUG and self._inited:
self._loop._debug_uv_handles_freed += 1

PyMem_RawFree(self._handle)
Expand All @@ -99,16 +100,17 @@ cdef class UVHandle:
if self._handle is not NULL:
self._free()

self._closed = 1

if UVLOOP_DEBUG:
name = self.__class__.__name__
if self._inited:
raise RuntimeError(
'_abort_init: {}._inited is set'.format(name))
if self._closed:
raise RuntimeError(
'_abort_init: {}._closed is set'.format(name))
try:
if UVLOOP_DEBUG:
name = self.__class__.__name__
if self._inited:
raise RuntimeError(
'_abort_init: {}._inited is set'.format(name))
if self._closed:
raise RuntimeError(
'_abort_init: {}._closed is set'.format(name))
finally:
self._closed = 1

cdef inline _finish_init(self):
self._inited = 1
Expand All @@ -117,7 +119,10 @@ cdef class UVHandle:
if self._loop._debug:
self._source_traceback = extract_stack()
if UVLOOP_DEBUG:
cls_name = self.__class__.__name__
self._loop._debug_uv_handles_total += 1
self._loop._debug_handles_total.update([cls_name])
self._loop._debug_handles_current.update([cls_name])

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

cls_name = self.__class__.__name__
loop._debug_handles_total.update([cls_name])
loop._debug_handles_current.update([cls_name])

self._loop = loop

cdef inline bint _is_alive(self):
Expand Down
30 changes: 19 additions & 11 deletions uvloop/handles/process.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@ cdef class UVProcess(UVHandle):

if _PyImport_ReleaseLock() < 0:
# See CPython/posixmodule.c for details
self._abort_init()
if err < 0:
self._abort_init()
else:
self._close()
raise RuntimeError('not holding the import lock')

if err < 0:
if UVLOOP_DEBUG and uv.uv_is_active(self._handle):
raise RuntimeError(
'active uv_process_t handle after failed uv_spawn')
self._abort_init()
raise convert_error(err)

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

fds_to_close = self._fds_to_close
self._fds_to_close = None
for fd in fds_to_close:
os_close(fd)

for fd in restore_inheritable:
os_set_inheritable(fd, False)

# asyncio caches the PID in BaseSubprocessTransport,
# so that the transport knows what the PID was even
# after the process is finished.
Expand All @@ -122,14 +136,6 @@ cdef class UVProcess(UVHandle):
# untrack this handle.
self._loop._track_process(self)

for fd in restore_inheritable:
os_set_inheritable(fd, False)

fds_to_close = self._fds_to_close
self._fds_to_close = None
for fd in fds_to_close:
os_close(fd)

if debug_flags & __PROCESS_DEBUG_SLEEP_AFTER_FORK:
time_sleep(1)

Expand Down Expand Up @@ -216,7 +222,7 @@ cdef class UVProcess(UVHandle):

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

assert len(io) == 3
for idx in range(3):
iocnt = &self.iocnt[idx]
if io[idx] is not None:
iocnt = &self.iocnt[idx]
iocnt.flags = uv.UV_INHERIT_FD
iocnt.data.fd = io[idx]
else:
iocnt.flags = uv.UV_IGNORE

cdef _call_connection_made(self, waiter):
try:
Expand Down
1 change: 1 addition & 0 deletions uvloop/includes/uv.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ cdef extern from "uv.h" nogil:
int uv_cancel(uv_req_t* req)

# Generic handler functions
int uv_is_active(const uv_handle_t* handle)
void uv_close(uv_handle_t* handle, uv_close_cb close_cb)
int uv_is_closing(const uv_handle_t* handle)
int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd)
Expand Down

0 comments on commit 4f6621e

Please sign in to comment.