Kaydet (Commit) b5c8cfa1 authored tarafından Vladimir Matveev's avatar Vladimir Matveev Kaydeden (comit) Andrew Svetlov

bpo-23057: add loop self socket as wakeup fd for signals (#11135)

üst e3666fc8
...@@ -10,6 +10,7 @@ import io ...@@ -10,6 +10,7 @@ import io
import os import os
import socket import socket
import warnings import warnings
import signal
from . import base_events from . import base_events
from . import constants from . import constants
...@@ -489,6 +490,8 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): ...@@ -489,6 +490,8 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
self._accept_futures = {} # socket file descriptor => Future self._accept_futures = {} # socket file descriptor => Future
proactor.set_loop(self) proactor.set_loop(self)
self._make_self_pipe() self._make_self_pipe()
self_no = self._csock.fileno()
signal.set_wakeup_fd(self_no)
def _make_socket_transport(self, sock, protocol, waiter=None, def _make_socket_transport(self, sock, protocol, waiter=None,
extra=None, server=None): extra=None, server=None):
...@@ -529,6 +532,7 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): ...@@ -529,6 +532,7 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
if self.is_closed(): if self.is_closed():
return return
signal.set_wakeup_fd(-1)
# Call these methods before closing the event loop (before calling # Call these methods before closing the event loop (before calling
# BaseEventLoop.close), because they can schedule callbacks with # BaseEventLoop.close), because they can schedule callbacks with
# call_soon(), which is forbidden when the event loop is closed. # call_soon(), which is forbidden when the event loop is closed.
...@@ -613,7 +617,6 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): ...@@ -613,7 +617,6 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
self._ssock.setblocking(False) self._ssock.setblocking(False)
self._csock.setblocking(False) self._csock.setblocking(False)
self._internal_fds += 1 self._internal_fds += 1
self.call_soon(self._loop_self_reading)
def _loop_self_reading(self, f=None): def _loop_self_reading(self, f=None):
try: try:
......
...@@ -308,6 +308,16 @@ class ProactorEventLoop(proactor_events.BaseProactorEventLoop): ...@@ -308,6 +308,16 @@ class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
proactor = IocpProactor() proactor = IocpProactor()
super().__init__(proactor) super().__init__(proactor)
def run_forever(self):
try:
assert self._self_reading_future is None
self.call_soon(self._loop_self_reading)
super().run_forever()
finally:
if self._self_reading_future is not None:
self._self_reading_future.cancel()
self._self_reading_future = None
async def create_pipe_connection(self, protocol_factory, address): async def create_pipe_connection(self, protocol_factory, address):
f = self._proactor.connect_pipe(address) f = self._proactor.connect_pipe(address)
pipe = await f pipe = await f
......
import sys
def do_in_child_process():
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
l = asyncio.get_event_loop()
def step(n):
try:
print(n)
sys.stdout.flush()
l.run_forever()
sys.exit(100)
except KeyboardInterrupt:
# ok
pass
except:
# error - use default exit code
sys.exit(200)
step(1)
step(2)
sys.exit(255)
def do_in_main_process():
import os
import signal
import subprocess
import time
from test.support.script_helper import spawn_python
ok = False
def step(p, expected):
s = p.stdout.readline()
if s != expected:
raise Exception(f"Unexpected line: got {s}, expected '{expected}'")
# ensure that child process gets to run_forever
time.sleep(0.5)
os.kill(p.pid, signal.CTRL_C_EVENT)
with spawn_python(__file__, "--child") as p:
try:
# ignore ctrl-c in current process
signal.signal(signal.SIGINT, signal.SIG_IGN)
step(p, b"1\r\n")
step(p, b"2\r\n")
exit_code = p.wait(timeout=5)
ok = exit_code = 255
except Exception as e:
sys.stderr.write(repr(e))
p.kill()
sys.exit(255 if ok else 1)
if __name__ == "__main__":
if len(sys.argv) == 1:
do_in_main_process()
else:
do_in_child_process()
...@@ -737,19 +737,19 @@ class BaseProactorEventLoopTests(test_utils.TestCase): ...@@ -737,19 +737,19 @@ class BaseProactorEventLoopTests(test_utils.TestCase):
with mock.patch('asyncio.proactor_events.socket.socketpair', with mock.patch('asyncio.proactor_events.socket.socketpair',
return_value=(self.ssock, self.csock)): return_value=(self.ssock, self.csock)):
self.loop = BaseProactorEventLoop(self.proactor) with mock.patch('signal.set_wakeup_fd'):
self.loop = BaseProactorEventLoop(self.proactor)
self.set_event_loop(self.loop) self.set_event_loop(self.loop)
@mock.patch.object(BaseProactorEventLoop, 'call_soon')
@mock.patch('asyncio.proactor_events.socket.socketpair') @mock.patch('asyncio.proactor_events.socket.socketpair')
def test_ctor(self, socketpair, call_soon): def test_ctor(self, socketpair):
ssock, csock = socketpair.return_value = ( ssock, csock = socketpair.return_value = (
mock.Mock(), mock.Mock()) mock.Mock(), mock.Mock())
loop = BaseProactorEventLoop(self.proactor) with mock.patch('signal.set_wakeup_fd'):
loop = BaseProactorEventLoop(self.proactor)
self.assertIs(loop._ssock, ssock) self.assertIs(loop._ssock, ssock)
self.assertIs(loop._csock, csock) self.assertIs(loop._csock, csock)
self.assertEqual(loop._internal_fds, 1) self.assertEqual(loop._internal_fds, 1)
call_soon.assert_called_with(loop._loop_self_reading)
loop.close() loop.close()
def test_close_self_pipe(self): def test_close_self_pipe(self):
......
import os import os
import signal
import socket import socket
import sys import sys
import subprocess
import time
import unittest import unittest
from unittest import mock from unittest import mock
...@@ -13,6 +16,7 @@ import _winapi ...@@ -13,6 +16,7 @@ import _winapi
import asyncio import asyncio
from asyncio import windows_events from asyncio import windows_events
from test.test_asyncio import utils as test_utils from test.test_asyncio import utils as test_utils
from test.support.script_helper import spawn_python
def tearDownModule(): def tearDownModule():
...@@ -33,6 +37,23 @@ class UpperProto(asyncio.Protocol): ...@@ -33,6 +37,23 @@ class UpperProto(asyncio.Protocol):
self.trans.close() self.trans.close()
class ProactorLoopCtrlC(test_utils.TestCase):
def test_ctrl_c(self):
from .test_ctrl_c_in_proactor_loop_helper import __file__ as f
# ctrl-c will be sent to all processes that share the same console
# in order to isolate the effect of raising ctrl-c we'll create
# a process with a new console
flags = subprocess.CREATE_NEW_CONSOLE
with spawn_python(f, creationflags=flags) as p:
try:
exit_code = p.wait(timeout=5)
self.assertEqual(exit_code, 255)
except:
p.kill()
raise
class ProactorTests(test_utils.TestCase): class ProactorTests(test_utils.TestCase):
def setUp(self): def setUp(self):
......
Unblock Proactor event loop when keyboard interrupt is received on Windows
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment