|
1 | 1 | """Base class for threads.""" |
2 | 2 |
|
| 3 | +import asyncio |
| 4 | +import sys |
3 | 5 | from threading import Thread |
4 | 6 |
|
5 | 7 | from tornado.ioloop import IOLoop |
|
8 | 10 | SHELL_CHANNEL_THREAD_NAME = "Shell channel" |
9 | 11 |
|
10 | 12 |
|
| 13 | +def make_selector_io_loop() -> IOLoop: |
| 14 | + """Create a non-current tornado ``IOLoop`` for an ipykernel service thread. |
| 15 | +
|
| 16 | + ipykernel runs its service channels -- control, IOPub, the shell channel and |
| 17 | + subshells -- on dedicated event loops in background threads. The process-wide |
| 18 | + asyncio loop on Windows is a ``ProactorEventLoop`` (so the main user-code loop |
| 19 | + can spawn asyncio subprocesses, see #1468/#1469), and Proactor has no native |
| 20 | + ``add_reader``. Tornado therefore drives a Proactor loop's zmq sockets through |
| 21 | + a helper "Tornado selector" thread. When a debugger suspends every thread at a |
| 22 | + breakpoint on Python >= 3.12 (``sys.monitoring``), that un-exempt helper thread |
| 23 | + freezes mid-wake and deadlocks the control/debug read path (#1469). |
| 24 | +
|
| 25 | + These service loops never need Proactor's subprocess support, so we keep them |
| 26 | + on a ``SelectorEventLoop``: it implements ``add_reader`` natively and needs no |
| 27 | + helper thread. Only the main/user-code loop stays on Proactor. On non-Windows |
| 28 | + platforms the default loop is already selector-based, so this is a no-op there. |
| 29 | + """ |
| 30 | + if sys.platform == "win32": |
| 31 | + return IOLoop(make_current=False, asyncio_loop=asyncio.SelectorEventLoop()) |
| 32 | + return IOLoop(make_current=False) |
| 33 | + |
| 34 | + |
11 | 35 | class BaseThread(Thread): |
12 | 36 | """Base class for threads.""" |
13 | 37 |
|
14 | 38 | def __init__(self, **kwargs): |
15 | 39 | """Initialize the thread.""" |
16 | 40 | super().__init__(**kwargs) |
17 | | - self.io_loop = IOLoop(make_current=False) |
| 41 | + self.io_loop = make_selector_io_loop() |
18 | 42 | self.pydev_do_not_trace = True |
19 | 43 | self.is_pydev_daemon_thread = True |
20 | 44 |
|
|
0 commit comments