Skip to content

Commit

Permalink
signals: Use 'sigaction' syscall for setting up signal handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
1st1 committed Apr 18, 2016
1 parent e7c7af4 commit 97a6450
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 13 deletions.
79 changes: 79 additions & 0 deletions tests/test_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,85 @@ def handler_hup():
class Test_UV_Signals(_TestSignal, tb.UVTestCase):
NEW_LOOP = 'uvloop.new_event_loop()'

def test_signals_restore(self):
# Test that uvloop restores signals installed with the signals
# module after the loop is done running.

async def runner():
PROG = R"""\
import asyncio
import uvloop
import signal
import time
srv = None
async def worker():
global srv
cb = lambda *args: None
srv = await asyncio.start_server(cb, '127.0.0.1', 0)
print('READY', flush=True)
def py_handler(signum, frame):
print('pyhandler', flush=True)
def aio_handler():
print('aiohandler', flush=True)
loop.stop()
signal.signal(signal.SIGUSR1, py_handler)
print('step1', flush=True)
print(input(), flush=True)
loop = """ + self.NEW_LOOP + """
loop.add_signal_handler(signal.SIGUSR1, aio_handler)
asyncio.set_event_loop(loop)
loop.create_task(worker())
loop.run_forever()
print('step3', flush=True)
print(input(), flush=True)
"""

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

ln = await proc.stdout.readline()
self.assertEqual(ln, b'step1\n')

proc.send_signal(signal.SIGUSR1)
ln = await proc.stdout.readline()
self.assertEqual(ln, b'pyhandler\n')

proc.stdin.write(b'test\n')
ln = await proc.stdout.readline()
self.assertEqual(ln, b'test\n')

ln = await proc.stdout.readline()
self.assertEqual(ln, b'READY\n')

proc.send_signal(signal.SIGUSR1)
ln = await proc.stdout.readline()
self.assertEqual(ln, b'aiohandler\n')

ln = await proc.stdout.readline()
self.assertEqual(ln, b'step3\n')

proc.send_signal(signal.SIGUSR1)
ln = await proc.stdout.readline()
self.assertEqual(ln, b'pyhandler\n')

proc.stdin.write(b'done\n')

out, err = await proc.communicate()
self.assertEqual(out, b'done\n')
self.assertEqual(err, b'')

self.loop.run_until_complete(runner())


class Test_AIO_Signals(_TestSignal, tb.AIOTestCase):
NEW_LOOP = 'asyncio.new_event_loop()'
35 changes: 22 additions & 13 deletions uvloop/os_signal.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from posix.signal cimport sigaction_t, sigaction
from libc.signal cimport SIG_DFL, SIG_IGN, SIG_ERR, sighandler_t, signal, SIGINT
from posix.signal cimport sigaction_t, sigaction, sigfillset
from libc.signal cimport SIG_DFL, SIG_IGN, sighandler_t, signal, SIGINT


cdef class SignalsStack:
Expand All @@ -22,16 +22,22 @@ cdef class SignalsStack:
cdef restore(self):
cdef:
sighandler_t sig
sigaction_t sa

if not self.saved:
raise RuntimeError("SignalsStack.save() wasn't called")

for i in range(MAX_SIG):
if self.signals[i] == NULL:
continue
sig = signal(i, self.signals[i])
if sig == SIG_ERR:
raise RuntimeError("Couldn't restore signal {}".format(i))

memset(&sa, 0, sizeof(sa))
if sigfillset(&sa.sa_mask):
raise RuntimeError(
'failed to restore signal (sigfillset failed)')
sa.sa_handler = self.signals[i]
if sigaction(i, &sa, NULL):
raise convert_error(-errno.errno)


cdef void __signal_handler_sigint(int sig) nogil:
Expand All @@ -41,11 +47,7 @@ cdef void __signal_handler_sigint(int sig) nogil:
# Python code here -- all '.' and '[]' operators work on
# C structs/pointers.

if sig != SIGINT:
return

if __main_loop__ is None or __main_loop__.py_signals is None:
# Shouldn't ever happen.
if sig != SIGINT or __main_loop__ is None:
return

if __main_loop__._executing_py_code and not __main_loop__._custom_sigint:
Expand All @@ -54,9 +56,16 @@ cdef void __signal_handler_sigint(int sig) nogil:

if __main_loop__.uv_signals is not None:
handle = __main_loop__.uv_signals.signals[sig]
if handle not in (SIG_DFL, SIG_IGN, SIG_ERR, NULL):
if handle is not NULL:
handle(sig) # void


cdef void __signal_set_sigint():
signal(SIGINT, <sighandler_t>__signal_handler_sigint)
cdef __signal_set_sigint():
cdef sigaction_t sa
memset(&sa, 0, sizeof(sa))
if sigfillset(&sa.sa_mask):
raise RuntimeError(
'failed to set SIGINT signal (sigfillset failed)')
sa.sa_handler = __signal_handler_sigint
if sigaction(SIGINT, &sa, NULL):
raise convert_error(-errno.errno)

0 comments on commit 97a6450

Please sign in to comment.