@@ -117,6 +117,7 @@ asyncio ships with the following built-in policies:
117117
118118 .. availability :: Windows.
119119
120+ .. _asyncio-watchers :
120121
121122Process Watchers
122123================
@@ -129,10 +130,11 @@ In asyncio, child processes are created with
129130:func: `create_subprocess_exec ` and :meth: `loop.subprocess_exec `
130131functions.
131132
132- asyncio defines the :class: `AbstractChildWatcher ` abstract base class,
133- which child watchers should implement, and has two different
134- implementations: :class: `SafeChildWatcher ` (configured to be used
135- by default) and :class: `FastChildWatcher `.
133+ asyncio defines the :class: `AbstractChildWatcher ` abstract base class, which child
134+ watchers should implement, and has four different implementations:
135+ :class: `ThreadedChildWatcher ` (configured to be used by default),
136+ :class: `MultiLoopChildWatcher `, :class: `SafeChildWatcher `, and
137+ :class: `FastChildWatcher `.
136138
137139See also the :ref: `Subprocess and Threads <asyncio-subprocess-threads >`
138140section.
@@ -184,23 +186,64 @@ implementation used by the asyncio event loop:
184186
185187 Note: loop may be ``None ``.
186188
189+ .. method :: is_active()
190+
191+ Return ``True `` if the watcher is ready to use.
192+
193+ Spawning a subprocess with *inactive * current child watcher raises
194+ :exc: `RuntimeError `.
195+
196+ .. versionadded :: 3.8
197+
187198 .. method :: close()
188199
189200 Close the watcher.
190201
191202 This method has to be called to ensure that underlying
192203 resources are cleaned-up.
193204
194- .. class :: SafeChildWatcher
205+ .. class :: ThreadedChildWatcher
206+
207+ This implementation starts a new waiting thread for every subprocess spawn.
208+
209+ It works reliably even when the asyncio event loop is run in a non-main OS thread.
210+
211+ There is no noticeable overhead when handling a big number of children (*O(1) * each
212+ time a child terminates), but stating a thread per process requires extra memory.
213+
214+ This watcher is used by default.
215+
216+ .. versionadded :: 3.8
195217
196- This implementation avoids disrupting other code spawning processes
218+ .. class :: MultiLoopChildWatcher
219+
220+ This implementation registers a :py:data: `SIGCHLD ` signal handler on
221+ instantiation. That can break third-party code that installs a custom handler for
222+ `SIGCHLD `. signal).
223+
224+ The watcher avoids disrupting other code spawning processes
197225 by polling every process explicitly on a :py:data: `SIGCHLD ` signal.
198226
199- This is a safe solution but it has a significant overhead when
227+ There is no limitation for running subprocesses from different threads once the
228+ watcher is installed.
229+
230+ The solution is safe but it has a significant overhead when
200231 handling a big number of processes (*O(n) * each time a
201232 :py:data: `SIGCHLD ` is received).
202233
203- asyncio uses this safe implementation by default.
234+ .. versionadded :: 3.8
235+
236+ .. class :: SafeChildWatcher
237+
238+ This implementation uses active event loop from the main thread to handle
239+ :py:data: `SIGCHLD ` signal. If the main thread has no running event loop another
240+ thread cannot spawn a subprocess (:exc: `RuntimeError ` is raised).
241+
242+ The watcher avoids disrupting other code spawning processes
243+ by polling every process explicitly on a :py:data: `SIGCHLD ` signal.
244+
245+ This solution is as safe as :class: `MultiLoopChildWatcher ` and has the same *O(N) *
246+ complexity but requires a running event loop in the main thread to work.
204247
205248.. class :: FastChildWatcher
206249
@@ -211,6 +254,9 @@ implementation used by the asyncio event loop:
211254 There is no noticeable overhead when handling a big number of
212255 children (*O(1) * each time a child terminates).
213256
257+ This solution requires a running event loop in the main thread to work, as
258+ :class: `SafeChildWatcher `.
259+
214260
215261Custom Policies
216262===============
0 commit comments