From fe5649c7b7bf52147480d6b1124a3d8e3597aee3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 17 Jul 2014 22:43:40 +0200 Subject: Python issue #21645, Tulip issue 192: Rewrite signal handling Since Python 3.3, the C signal handler writes the signal number into the wakeup file descriptor and then schedules the Python call using Py_AddPendingCall(). asyncio uses the wakeup file descriptor to wake up the event loop, and relies on Py_AddPendingCall() to schedule the final callback with call_soon(). If the C signal handler is called in a thread different than the thread of the event loop, the loop is awaken but Py_AddPendingCall() was not called yet. In this case, the event loop has nothing to do and go to sleep again. Py_AddPendingCall() is called while the event loop is sleeping again and so the final callback is not scheduled immediatly. This patch changes how asyncio handles signals. Instead of relying on Py_AddPendingCall() and the wakeup file descriptor, asyncio now only relies on the wakeup file descriptor. asyncio reads signal numbers from the wakeup file descriptor to call its signal handler. --- Lib/asyncio/unix_events.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'Lib/asyncio/unix_events.py') diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 4ba4f49cb99..73a85c11c0d 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -31,6 +31,11 @@ if sys.platform == 'win32': # pragma: no cover raise ImportError('Signals are not really supported on Windows') +def _sighandler_noop(signum, frame): + """Dummy signal handler.""" + pass + + class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): """Unix event loop. @@ -49,6 +54,13 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): for sig in list(self._signal_handlers): self.remove_signal_handler(sig) + def _process_self_data(self, data): + for signum in data: + if not signum: + # ignore null bytes written by _write_to_self() + continue + self._handle_signal(signum) + def add_signal_handler(self, sig, callback, *args): """Add a handler for a signal. UNIX only. @@ -69,7 +81,11 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): self._signal_handlers[sig] = handle try: - signal.signal(sig, self._handle_signal) + # Register a dummy signal handler to ask Python to write the signal + # number in the wakup file descriptor. _process_self_data() will + # read signal numbers from this file descriptor to handle signals. + signal.signal(sig, _sighandler_noop) + # Set SA_RESTART to limit EINTR occurrences. signal.siginterrupt(sig, False) except OSError as exc: @@ -85,7 +101,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): else: raise - def _handle_signal(self, sig, arg): + def _handle_signal(self, sig): """Internal helper that is the actual signal handler.""" handle = self._signal_handlers.get(sig) if handle is None: -- cgit v1.2.3