diff -r 47618b00405b Lib/asyncio/base_events.py --- a/Lib/asyncio/base_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/asyncio/base_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -543,11 +543,6 @@ else: self._ready.append(handle) - def _add_callback_signalsafe(self, handle): - """Like _add_callback() but called from a signal handler.""" - self._add_callback(handle) - self._write_to_self() - def _run_once(self): """Run one full iteration of the event loop. diff -r 47618b00405b Lib/asyncio/proactor_events.py --- a/Lib/asyncio/proactor_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/asyncio/proactor_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -319,7 +319,7 @@ f.add_done_callback(self._loop_self_reading) def _write_to_self(self): - self._csock.send(b'x') + self._csock.send(b'\x00') def _start_serving(self, protocol_factory, sock, ssl=None, server=None): assert not ssl, 'IocpEventLoop is incompatible with SSL.' diff -r 47618b00405b Lib/asyncio/selector_events.py --- a/Lib/asyncio/selector_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/asyncio/selector_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -84,7 +84,8 @@ def _write_to_self(self): try: - self._csock.send(b'x') + # send a payload that can't be a signal number + self._csock.send(b'\x00') except (BlockingIOError, InterruptedError): pass diff -r 47618b00405b Lib/asyncio/unix_events.py --- a/Lib/asyncio/unix_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/asyncio/unix_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -8,6 +8,7 @@ import signal import socket import stat +import struct import subprocess import sys @@ -71,8 +72,10 @@ handle = events.make_handle(callback, args) self._signal_handlers[sig] = handle + # setup a dummy signal handler: we just want to be notified through the + # wakeup FD try: - signal.signal(sig, self._handle_signal) + signal.signal(sig, lambda *args: None) except OSError as exc: del self._signal_handlers[sig] if not self._signal_handlers: @@ -86,15 +89,22 @@ else: raise - def _handle_signal(self, sig, arg): - """Internal helper that is the actual signal handler.""" - handle = self._signal_handlers.get(sig) - if handle is None: - return # Assume it's some race condition. - if handle._cancelled: - self.remove_signal_handler(sig) # Remove it properly. + def _read_from_self(self): + try: + payload = self._ssock.recv(1) + sig = struct.unpack('B', payload)[0] + except (BlockingIOError, InterruptedError, struct.error): + pass else: - self._add_callback_signalsafe(handle) + handle = self._signal_handlers.get(sig) + if handle is None: + # it was either a call to _write_to_self()), or some race + # condition + return + if handle._cancelled: + self.remove_signal_handler(sig) # Remove it properly. + else: + self._add_callback(handle) def remove_signal_handler(self, sig): """Remove a handler for a signal. UNIX only. diff -r 47618b00405b Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/test/test_asyncio/test_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -397,6 +397,7 @@ def my_handler(): nonlocal caught caught += 1 + self.loop.stop() # Check error behavior first. self.assertRaises( @@ -425,7 +426,7 @@ self.loop.add_signal_handler(signal.SIGINT, my_handler) test_utils.run_briefly(self.loop) os.kill(os.getpid(), signal.SIGINT) - test_utils.run_briefly(self.loop) + self.loop.run_forever() self.assertEqual(caught, 1) # Removing it should restore the default handler. self.assertTrue(self.loop.remove_signal_handler(signal.SIGINT)) diff -r 47618b00405b Lib/test/test_asyncio/test_proactor_events.py --- a/Lib/test/test_asyncio/test_proactor_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/test/test_asyncio/test_proactor_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -427,7 +427,7 @@ def test_write_to_self(self): self.loop._write_to_self() - self.csock.send.assert_called_with(b'x') + self.csock.send.assert_called_with(b'\x00') def test_process_events(self): self.loop._process_events([]) diff -r 47618b00405b Lib/test/test_asyncio/test_unix_events.py --- a/Lib/test/test_asyncio/test_unix_events.py Sat Oct 19 10:45:48 2013 +0300 +++ b/Lib/test/test_asyncio/test_unix_events.py Sat Oct 19 21:39:04 2013 +0200 @@ -37,17 +37,6 @@ self.assertRaises( ValueError, self.loop._check_signal, signal.NSIG + 1) - def test_handle_signal_no_handler(self): - self.loop._handle_signal(signal.NSIG + 1, ()) - - def test_handle_signal_cancelled_handler(self): - h = events.Handle(unittest.mock.Mock(), ()) - h.cancel() - self.loop._signal_handlers[signal.NSIG + 1] = h - self.loop.remove_signal_handler = unittest.mock.Mock() - self.loop._handle_signal(signal.NSIG + 1, ()) - self.loop.remove_signal_handler.assert_called_with(signal.NSIG + 1) - @unittest.mock.patch('asyncio.unix_events.signal') def test_add_signal_handler_setup_error(self, m_signal): m_signal.NSIG = signal.NSIG