Skip to content

Commit

Permalink
Keep behavior of run_* consistent with event loops bundled in CPython…
Browse files Browse the repository at this point in the history
…: ensure that wakeup fd is non-modified if it was not changed before i.e. via calling add_signal_handler
  • Loading branch information
vladima authored and 1st1 committed Oct 24, 2019
1 parent 1765691 commit 48d376d
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 5 deletions.
40 changes: 40 additions & 0 deletions tests/test_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,46 @@ async def coro():
with self.assertRaisesRegex(TypeError, 'coroutines cannot be used'):
self.loop.add_signal_handler(signal.SIGHUP, coro)

def test_wakeup_fd_unchanged(self):
async def runner():
PROG = R"""\
import uvloop
import signal
import asyncio
def get_wakeup_fd():
fd = signal.set_wakeup_fd(-1)
signal.set_wakeup_fd(fd)
return fd
async def f(): pass
fd0 = get_wakeup_fd()
loop = """ + self.NEW_LOOP + """
try:
asyncio.set_event_loop(loop)
loop.run_until_complete(f())
fd1 = get_wakeup_fd()
finally:
loop.close()
print(fd0 == fd1, flush=True)
"""

proc = await asyncio.create_subprocess_exec(
sys.executable, b'-W', b'ignore', b'-c', PROG,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
loop=self.loop)

out, err = await proc.communicate()
self.assertEqual(err, b'')
self.assertIn(b'True', out)

self.loop.run_until_complete(runner())


class Test_UV_Signals(_TestSignal, tb.UVTestCase):
NEW_LOOP = 'uvloop.new_event_loop()'
Expand Down
18 changes: 13 additions & 5 deletions uvloop/loop.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,16 @@ cdef class Loop:
self._debug_exception_handler_cnt = 0

cdef _setup_signals(self):
cdef int old_wakeup_fd

if self._listening_signals:
return

self._ssock, self._csock = socket_socketpair()
self._ssock.setblocking(False)
self._csock.setblocking(False)
try:
_set_signal_wakeup_fd(self._csock.fileno())
old_wakeup_fd = _set_signal_wakeup_fd(self._csock.fileno())
except (OSError, ValueError):
# Not the main thread
self._ssock.close()
Expand All @@ -257,10 +259,12 @@ cdef class Loop:
return

self._listening_signals = True
return old_wakeup_fd

cdef _recv_signals_start(self):
cdef object old_wakeup_fd = None
if self._ssock is None:
self._setup_signals()
old_wakeup_fd = self._setup_signals()
if self._ssock is None:
# Not the main thread.
return
Expand All @@ -272,6 +276,7 @@ cdef class Loop:
"Loop._read_from_self",
<method_t>self._read_from_self,
self))
return old_wakeup_fd

cdef _recv_signals_stop(self):
if self._ssock is None:
Expand Down Expand Up @@ -445,6 +450,7 @@ cdef class Loop:

cdef _run(self, uv.uv_run_mode mode):
cdef int err
cdef object old_wakeup_fd

if self._closed == 1:
raise RuntimeError('unable to start the loop; it was closed')
Expand All @@ -467,7 +473,7 @@ cdef class Loop:
self.handler_check__exec_writes.start()
self.handler_idle.start()

self._recv_signals_start()
old_wakeup_fd = self._recv_signals_start()

if aio_set_running_loop is not None:
aio_set_running_loop(self)
Expand All @@ -478,6 +484,8 @@ cdef class Loop:
aio_set_running_loop(None)

self._recv_signals_stop()
if old_wakeup_fd is not None:
signal_set_wakeup_fd(old_wakeup_fd)

self.handler_check__exec_writes.stop()
self.handler_idle.stop()
Expand Down Expand Up @@ -3218,9 +3226,9 @@ cdef __install_pymem():

cdef _set_signal_wakeup_fd(fd):
if PY37 and fd >= 0:
signal_set_wakeup_fd(fd, warn_on_full_buffer=False)
return signal_set_wakeup_fd(fd, warn_on_full_buffer=False)
else:
signal_set_wakeup_fd(fd)
return signal_set_wakeup_fd(fd)


cdef _warn_with_source(msg, cls, source):
Expand Down

0 comments on commit 48d376d

Please sign in to comment.