66Compatibility for [add|remove]_[reader|writer] where unavailable (Proactor).
77
88Runs select in a background thread.
9+ _Only_ `select.select` is called in the background thread.
10+
11+ Callbacks are all handled back in the event loop's thread,
12+ as scheduled by `loop.call_soon_threadsafe`.
913
1014Adapted from Tornado 6.5.2
1115"""
@@ -61,10 +65,8 @@ class SelectorThread:
6165
6266 Instances of this class start a second thread to run a selector.
6367 This thread is completely hidden from the user;
64- all callbacks are run on the wrapped event loop's thread.
65-
66- Typically used via ``AddThreadSelectorEventLoop``,
67- but can be attached to a running asyncio loop.
68+ all callbacks are run on the wrapped event loop's thread
69+ via :meth:`loop.call_soon_threadsafe`.
6870 """
6971
7072 _closed = False
@@ -140,6 +142,7 @@ async def _thread_manager(self) -> typing.AsyncGenerator[None, None]:
140142 raise
141143
142144 def _wake_selector (self ) -> None :
145+ """Wake the selector thread from another thread."""
143146 if self ._closed :
144147 return
145148 try :
@@ -148,12 +151,18 @@ def _wake_selector(self) -> None:
148151 pass
149152
150153 def _consume_waker (self ) -> None :
154+ """Consume messages sent via _wake_selector."""
151155 try :
152156 self ._waker_r .recv (1024 )
153157 except BlockingIOError :
154158 pass
155159
156160 def _start_select (self ) -> None :
161+ """Start select waiting for events.
162+
163+ Called from the event loop thread,
164+ schedules select to be called in the background thread.
165+ """
157166 # Capture reader and writer sets here in the event loop
158167 # thread to avoid any problems with concurrent
159168 # modification while the select loop uses them.
@@ -163,6 +172,12 @@ def _start_select(self) -> None:
163172 self ._select_cond .notify ()
164173
165174 def _run_select (self ) -> None :
175+ """The main function of the select thread.
176+
177+ Runs `select.select()` until `_closing_selector` attribute is set (typically by `close()`).
178+ Schedules handling of `select.select` output on the main thread
179+ via `loop.call_soon_threadsafe()`.
180+ """
166181 while not self ._closing_selector :
167182 with self ._select_cond :
168183 while self ._select_args is None and not self ._closing_selector :
@@ -223,6 +238,10 @@ def _run_select(self) -> None:
223238 def _handle_select (
224239 self , rs : list [_FileDescriptorLike ], ws : list [_FileDescriptorLike ]
225240 ) -> None :
241+ """Handle the result of select.select.
242+
243+ This method is called on the event loop thread via `call_soon_threadsafe`.
244+ """
226245 for r in rs :
227246 self ._handle_event (r , self ._readers )
228247 for w in ws :
@@ -234,6 +253,11 @@ def _handle_event(
234253 fd : _FileDescriptorLike ,
235254 cb_map : dict [int , tuple [_FileDescriptorLike , Callable ]],
236255 ) -> None :
256+ """Handle one callback event.
257+
258+ This method is called on the event loop thread via `call_soon_threadsafe` (from `_handle_select`),
259+ so exception handler wrappers, etc. are applied.
260+ """
237261 try :
238262 fileobj , callback = cb_map [fd ]
239263 except KeyError :
0 commit comments