signals: Use 'sigaction' syscall for setting up signal handlers

This commit is contained in:
Yury Selivanov 2016-04-18 17:52:28 -04:00
parent e7c7af4497
commit 97a64505d1
2 changed files with 101 additions and 13 deletions

View File

@ -227,6 +227,85 @@ loop.run_forever()
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()'

View File

@ -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:
@ -22,6 +22,7 @@ cdef class SignalsStack:
cdef restore(self):
cdef:
sighandler_t sig
sigaction_t sa
if not self.saved:
raise RuntimeError("SignalsStack.save() wasn't called")
@ -29,9 +30,14 @@ cdef class SignalsStack:
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:
@ -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:
@ -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)