xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/asyncio/unix_events.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Selector event loop for Unix with signal handling."""
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Workerimport errno
4*cda5da8dSAndroid Build Coastguard Workerimport io
5*cda5da8dSAndroid Build Coastguard Workerimport itertools
6*cda5da8dSAndroid Build Coastguard Workerimport os
7*cda5da8dSAndroid Build Coastguard Workerimport selectors
8*cda5da8dSAndroid Build Coastguard Workerimport signal
9*cda5da8dSAndroid Build Coastguard Workerimport socket
10*cda5da8dSAndroid Build Coastguard Workerimport stat
11*cda5da8dSAndroid Build Coastguard Workerimport subprocess
12*cda5da8dSAndroid Build Coastguard Workerimport sys
13*cda5da8dSAndroid Build Coastguard Workerimport threading
14*cda5da8dSAndroid Build Coastguard Workerimport warnings
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard Workerfrom . import base_events
17*cda5da8dSAndroid Build Coastguard Workerfrom . import base_subprocess
18*cda5da8dSAndroid Build Coastguard Workerfrom . import constants
19*cda5da8dSAndroid Build Coastguard Workerfrom . import coroutines
20*cda5da8dSAndroid Build Coastguard Workerfrom . import events
21*cda5da8dSAndroid Build Coastguard Workerfrom . import exceptions
22*cda5da8dSAndroid Build Coastguard Workerfrom . import futures
23*cda5da8dSAndroid Build Coastguard Workerfrom . import selector_events
24*cda5da8dSAndroid Build Coastguard Workerfrom . import tasks
25*cda5da8dSAndroid Build Coastguard Workerfrom . import transports
26*cda5da8dSAndroid Build Coastguard Workerfrom .log import logger
27*cda5da8dSAndroid Build Coastguard Worker
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Worker__all__ = (
30*cda5da8dSAndroid Build Coastguard Worker    'SelectorEventLoop',
31*cda5da8dSAndroid Build Coastguard Worker    'AbstractChildWatcher', 'SafeChildWatcher',
32*cda5da8dSAndroid Build Coastguard Worker    'FastChildWatcher', 'PidfdChildWatcher',
33*cda5da8dSAndroid Build Coastguard Worker    'MultiLoopChildWatcher', 'ThreadedChildWatcher',
34*cda5da8dSAndroid Build Coastguard Worker    'DefaultEventLoopPolicy',
35*cda5da8dSAndroid Build Coastguard Worker)
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard Worker
38*cda5da8dSAndroid Build Coastguard Workerif sys.platform == 'win32':  # pragma: no cover
39*cda5da8dSAndroid Build Coastguard Worker    raise ImportError('Signals are not really supported on Windows')
40*cda5da8dSAndroid Build Coastguard Worker
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard Workerdef _sighandler_noop(signum, frame):
43*cda5da8dSAndroid Build Coastguard Worker    """Dummy signal handler."""
44*cda5da8dSAndroid Build Coastguard Worker    pass
45*cda5da8dSAndroid Build Coastguard Worker
46*cda5da8dSAndroid Build Coastguard Worker
47*cda5da8dSAndroid Build Coastguard Workerdef waitstatus_to_exitcode(status):
48*cda5da8dSAndroid Build Coastguard Worker    try:
49*cda5da8dSAndroid Build Coastguard Worker        return os.waitstatus_to_exitcode(status)
50*cda5da8dSAndroid Build Coastguard Worker    except ValueError:
51*cda5da8dSAndroid Build Coastguard Worker        # The child exited, but we don't understand its status.
52*cda5da8dSAndroid Build Coastguard Worker        # This shouldn't happen, but if it does, let's just
53*cda5da8dSAndroid Build Coastguard Worker        # return that status; perhaps that helps debug it.
54*cda5da8dSAndroid Build Coastguard Worker        return status
55*cda5da8dSAndroid Build Coastguard Worker
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Workerclass _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
58*cda5da8dSAndroid Build Coastguard Worker    """Unix event loop.
59*cda5da8dSAndroid Build Coastguard Worker
60*cda5da8dSAndroid Build Coastguard Worker    Adds signal handling and UNIX Domain Socket support to SelectorEventLoop.
61*cda5da8dSAndroid Build Coastguard Worker    """
62*cda5da8dSAndroid Build Coastguard Worker
63*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, selector=None):
64*cda5da8dSAndroid Build Coastguard Worker        super().__init__(selector)
65*cda5da8dSAndroid Build Coastguard Worker        self._signal_handlers = {}
66*cda5da8dSAndroid Build Coastguard Worker
67*cda5da8dSAndroid Build Coastguard Worker    def close(self):
68*cda5da8dSAndroid Build Coastguard Worker        super().close()
69*cda5da8dSAndroid Build Coastguard Worker        if not sys.is_finalizing():
70*cda5da8dSAndroid Build Coastguard Worker            for sig in list(self._signal_handlers):
71*cda5da8dSAndroid Build Coastguard Worker                self.remove_signal_handler(sig)
72*cda5da8dSAndroid Build Coastguard Worker        else:
73*cda5da8dSAndroid Build Coastguard Worker            if self._signal_handlers:
74*cda5da8dSAndroid Build Coastguard Worker                warnings.warn(f"Closing the loop {self!r} "
75*cda5da8dSAndroid Build Coastguard Worker                              f"on interpreter shutdown "
76*cda5da8dSAndroid Build Coastguard Worker                              f"stage, skipping signal handlers removal",
77*cda5da8dSAndroid Build Coastguard Worker                              ResourceWarning,
78*cda5da8dSAndroid Build Coastguard Worker                              source=self)
79*cda5da8dSAndroid Build Coastguard Worker                self._signal_handlers.clear()
80*cda5da8dSAndroid Build Coastguard Worker
81*cda5da8dSAndroid Build Coastguard Worker    def _process_self_data(self, data):
82*cda5da8dSAndroid Build Coastguard Worker        for signum in data:
83*cda5da8dSAndroid Build Coastguard Worker            if not signum:
84*cda5da8dSAndroid Build Coastguard Worker                # ignore null bytes written by _write_to_self()
85*cda5da8dSAndroid Build Coastguard Worker                continue
86*cda5da8dSAndroid Build Coastguard Worker            self._handle_signal(signum)
87*cda5da8dSAndroid Build Coastguard Worker
88*cda5da8dSAndroid Build Coastguard Worker    def add_signal_handler(self, sig, callback, *args):
89*cda5da8dSAndroid Build Coastguard Worker        """Add a handler for a signal.  UNIX only.
90*cda5da8dSAndroid Build Coastguard Worker
91*cda5da8dSAndroid Build Coastguard Worker        Raise ValueError if the signal number is invalid or uncatchable.
92*cda5da8dSAndroid Build Coastguard Worker        Raise RuntimeError if there is a problem setting up the handler.
93*cda5da8dSAndroid Build Coastguard Worker        """
94*cda5da8dSAndroid Build Coastguard Worker        if (coroutines.iscoroutine(callback) or
95*cda5da8dSAndroid Build Coastguard Worker                coroutines.iscoroutinefunction(callback)):
96*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("coroutines cannot be used "
97*cda5da8dSAndroid Build Coastguard Worker                            "with add_signal_handler()")
98*cda5da8dSAndroid Build Coastguard Worker        self._check_signal(sig)
99*cda5da8dSAndroid Build Coastguard Worker        self._check_closed()
100*cda5da8dSAndroid Build Coastguard Worker        try:
101*cda5da8dSAndroid Build Coastguard Worker            # set_wakeup_fd() raises ValueError if this is not the
102*cda5da8dSAndroid Build Coastguard Worker            # main thread.  By calling it early we ensure that an
103*cda5da8dSAndroid Build Coastguard Worker            # event loop running in another thread cannot add a signal
104*cda5da8dSAndroid Build Coastguard Worker            # handler.
105*cda5da8dSAndroid Build Coastguard Worker            signal.set_wakeup_fd(self._csock.fileno())
106*cda5da8dSAndroid Build Coastguard Worker        except (ValueError, OSError) as exc:
107*cda5da8dSAndroid Build Coastguard Worker            raise RuntimeError(str(exc))
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker        handle = events.Handle(callback, args, self, None)
110*cda5da8dSAndroid Build Coastguard Worker        self._signal_handlers[sig] = handle
111*cda5da8dSAndroid Build Coastguard Worker
112*cda5da8dSAndroid Build Coastguard Worker        try:
113*cda5da8dSAndroid Build Coastguard Worker            # Register a dummy signal handler to ask Python to write the signal
114*cda5da8dSAndroid Build Coastguard Worker            # number in the wakeup file descriptor. _process_self_data() will
115*cda5da8dSAndroid Build Coastguard Worker            # read signal numbers from this file descriptor to handle signals.
116*cda5da8dSAndroid Build Coastguard Worker            signal.signal(sig, _sighandler_noop)
117*cda5da8dSAndroid Build Coastguard Worker
118*cda5da8dSAndroid Build Coastguard Worker            # Set SA_RESTART to limit EINTR occurrences.
119*cda5da8dSAndroid Build Coastguard Worker            signal.siginterrupt(sig, False)
120*cda5da8dSAndroid Build Coastguard Worker        except OSError as exc:
121*cda5da8dSAndroid Build Coastguard Worker            del self._signal_handlers[sig]
122*cda5da8dSAndroid Build Coastguard Worker            if not self._signal_handlers:
123*cda5da8dSAndroid Build Coastguard Worker                try:
124*cda5da8dSAndroid Build Coastguard Worker                    signal.set_wakeup_fd(-1)
125*cda5da8dSAndroid Build Coastguard Worker                except (ValueError, OSError) as nexc:
126*cda5da8dSAndroid Build Coastguard Worker                    logger.info('set_wakeup_fd(-1) failed: %s', nexc)
127*cda5da8dSAndroid Build Coastguard Worker
128*cda5da8dSAndroid Build Coastguard Worker            if exc.errno == errno.EINVAL:
129*cda5da8dSAndroid Build Coastguard Worker                raise RuntimeError(f'sig {sig} cannot be caught')
130*cda5da8dSAndroid Build Coastguard Worker            else:
131*cda5da8dSAndroid Build Coastguard Worker                raise
132*cda5da8dSAndroid Build Coastguard Worker
133*cda5da8dSAndroid Build Coastguard Worker    def _handle_signal(self, sig):
134*cda5da8dSAndroid Build Coastguard Worker        """Internal helper that is the actual signal handler."""
135*cda5da8dSAndroid Build Coastguard Worker        handle = self._signal_handlers.get(sig)
136*cda5da8dSAndroid Build Coastguard Worker        if handle is None:
137*cda5da8dSAndroid Build Coastguard Worker            return  # Assume it's some race condition.
138*cda5da8dSAndroid Build Coastguard Worker        if handle._cancelled:
139*cda5da8dSAndroid Build Coastguard Worker            self.remove_signal_handler(sig)  # Remove it properly.
140*cda5da8dSAndroid Build Coastguard Worker        else:
141*cda5da8dSAndroid Build Coastguard Worker            self._add_callback_signalsafe(handle)
142*cda5da8dSAndroid Build Coastguard Worker
143*cda5da8dSAndroid Build Coastguard Worker    def remove_signal_handler(self, sig):
144*cda5da8dSAndroid Build Coastguard Worker        """Remove a handler for a signal.  UNIX only.
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Worker        Return True if a signal handler was removed, False if not.
147*cda5da8dSAndroid Build Coastguard Worker        """
148*cda5da8dSAndroid Build Coastguard Worker        self._check_signal(sig)
149*cda5da8dSAndroid Build Coastguard Worker        try:
150*cda5da8dSAndroid Build Coastguard Worker            del self._signal_handlers[sig]
151*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
152*cda5da8dSAndroid Build Coastguard Worker            return False
153*cda5da8dSAndroid Build Coastguard Worker
154*cda5da8dSAndroid Build Coastguard Worker        if sig == signal.SIGINT:
155*cda5da8dSAndroid Build Coastguard Worker            handler = signal.default_int_handler
156*cda5da8dSAndroid Build Coastguard Worker        else:
157*cda5da8dSAndroid Build Coastguard Worker            handler = signal.SIG_DFL
158*cda5da8dSAndroid Build Coastguard Worker
159*cda5da8dSAndroid Build Coastguard Worker        try:
160*cda5da8dSAndroid Build Coastguard Worker            signal.signal(sig, handler)
161*cda5da8dSAndroid Build Coastguard Worker        except OSError as exc:
162*cda5da8dSAndroid Build Coastguard Worker            if exc.errno == errno.EINVAL:
163*cda5da8dSAndroid Build Coastguard Worker                raise RuntimeError(f'sig {sig} cannot be caught')
164*cda5da8dSAndroid Build Coastguard Worker            else:
165*cda5da8dSAndroid Build Coastguard Worker                raise
166*cda5da8dSAndroid Build Coastguard Worker
167*cda5da8dSAndroid Build Coastguard Worker        if not self._signal_handlers:
168*cda5da8dSAndroid Build Coastguard Worker            try:
169*cda5da8dSAndroid Build Coastguard Worker                signal.set_wakeup_fd(-1)
170*cda5da8dSAndroid Build Coastguard Worker            except (ValueError, OSError) as exc:
171*cda5da8dSAndroid Build Coastguard Worker                logger.info('set_wakeup_fd(-1) failed: %s', exc)
172*cda5da8dSAndroid Build Coastguard Worker
173*cda5da8dSAndroid Build Coastguard Worker        return True
174*cda5da8dSAndroid Build Coastguard Worker
175*cda5da8dSAndroid Build Coastguard Worker    def _check_signal(self, sig):
176*cda5da8dSAndroid Build Coastguard Worker        """Internal helper to validate a signal.
177*cda5da8dSAndroid Build Coastguard Worker
178*cda5da8dSAndroid Build Coastguard Worker        Raise ValueError if the signal number is invalid or uncatchable.
179*cda5da8dSAndroid Build Coastguard Worker        Raise RuntimeError if there is a problem setting up the handler.
180*cda5da8dSAndroid Build Coastguard Worker        """
181*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(sig, int):
182*cda5da8dSAndroid Build Coastguard Worker            raise TypeError(f'sig must be an int, not {sig!r}')
183*cda5da8dSAndroid Build Coastguard Worker
184*cda5da8dSAndroid Build Coastguard Worker        if sig not in signal.valid_signals():
185*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(f'invalid signal number {sig}')
186*cda5da8dSAndroid Build Coastguard Worker
187*cda5da8dSAndroid Build Coastguard Worker    def _make_read_pipe_transport(self, pipe, protocol, waiter=None,
188*cda5da8dSAndroid Build Coastguard Worker                                  extra=None):
189*cda5da8dSAndroid Build Coastguard Worker        return _UnixReadPipeTransport(self, pipe, protocol, waiter, extra)
190*cda5da8dSAndroid Build Coastguard Worker
191*cda5da8dSAndroid Build Coastguard Worker    def _make_write_pipe_transport(self, pipe, protocol, waiter=None,
192*cda5da8dSAndroid Build Coastguard Worker                                   extra=None):
193*cda5da8dSAndroid Build Coastguard Worker        return _UnixWritePipeTransport(self, pipe, protocol, waiter, extra)
194*cda5da8dSAndroid Build Coastguard Worker
195*cda5da8dSAndroid Build Coastguard Worker    async def _make_subprocess_transport(self, protocol, args, shell,
196*cda5da8dSAndroid Build Coastguard Worker                                         stdin, stdout, stderr, bufsize,
197*cda5da8dSAndroid Build Coastguard Worker                                         extra=None, **kwargs):
198*cda5da8dSAndroid Build Coastguard Worker        with events.get_child_watcher() as watcher:
199*cda5da8dSAndroid Build Coastguard Worker            if not watcher.is_active():
200*cda5da8dSAndroid Build Coastguard Worker                # Check early.
201*cda5da8dSAndroid Build Coastguard Worker                # Raising exception before process creation
202*cda5da8dSAndroid Build Coastguard Worker                # prevents subprocess execution if the watcher
203*cda5da8dSAndroid Build Coastguard Worker                # is not ready to handle it.
204*cda5da8dSAndroid Build Coastguard Worker                raise RuntimeError("asyncio.get_child_watcher() is not activated, "
205*cda5da8dSAndroid Build Coastguard Worker                                   "subprocess support is not installed.")
206*cda5da8dSAndroid Build Coastguard Worker            waiter = self.create_future()
207*cda5da8dSAndroid Build Coastguard Worker            transp = _UnixSubprocessTransport(self, protocol, args, shell,
208*cda5da8dSAndroid Build Coastguard Worker                                              stdin, stdout, stderr, bufsize,
209*cda5da8dSAndroid Build Coastguard Worker                                              waiter=waiter, extra=extra,
210*cda5da8dSAndroid Build Coastguard Worker                                              **kwargs)
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker            watcher.add_child_handler(transp.get_pid(),
213*cda5da8dSAndroid Build Coastguard Worker                                      self._child_watcher_callback, transp)
214*cda5da8dSAndroid Build Coastguard Worker            try:
215*cda5da8dSAndroid Build Coastguard Worker                await waiter
216*cda5da8dSAndroid Build Coastguard Worker            except (SystemExit, KeyboardInterrupt):
217*cda5da8dSAndroid Build Coastguard Worker                raise
218*cda5da8dSAndroid Build Coastguard Worker            except BaseException:
219*cda5da8dSAndroid Build Coastguard Worker                transp.close()
220*cda5da8dSAndroid Build Coastguard Worker                await transp._wait()
221*cda5da8dSAndroid Build Coastguard Worker                raise
222*cda5da8dSAndroid Build Coastguard Worker
223*cda5da8dSAndroid Build Coastguard Worker        return transp
224*cda5da8dSAndroid Build Coastguard Worker
225*cda5da8dSAndroid Build Coastguard Worker    def _child_watcher_callback(self, pid, returncode, transp):
226*cda5da8dSAndroid Build Coastguard Worker        # Skip one iteration for callbacks to be executed
227*cda5da8dSAndroid Build Coastguard Worker        self.call_soon_threadsafe(self.call_soon, transp._process_exited, returncode)
228*cda5da8dSAndroid Build Coastguard Worker
229*cda5da8dSAndroid Build Coastguard Worker    async def create_unix_connection(
230*cda5da8dSAndroid Build Coastguard Worker            self, protocol_factory, path=None, *,
231*cda5da8dSAndroid Build Coastguard Worker            ssl=None, sock=None,
232*cda5da8dSAndroid Build Coastguard Worker            server_hostname=None,
233*cda5da8dSAndroid Build Coastguard Worker            ssl_handshake_timeout=None,
234*cda5da8dSAndroid Build Coastguard Worker            ssl_shutdown_timeout=None):
235*cda5da8dSAndroid Build Coastguard Worker        assert server_hostname is None or isinstance(server_hostname, str)
236*cda5da8dSAndroid Build Coastguard Worker        if ssl:
237*cda5da8dSAndroid Build Coastguard Worker            if server_hostname is None:
238*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
239*cda5da8dSAndroid Build Coastguard Worker                    'you have to pass server_hostname when using ssl')
240*cda5da8dSAndroid Build Coastguard Worker        else:
241*cda5da8dSAndroid Build Coastguard Worker            if server_hostname is not None:
242*cda5da8dSAndroid Build Coastguard Worker                raise ValueError('server_hostname is only meaningful with ssl')
243*cda5da8dSAndroid Build Coastguard Worker            if ssl_handshake_timeout is not None:
244*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
245*cda5da8dSAndroid Build Coastguard Worker                    'ssl_handshake_timeout is only meaningful with ssl')
246*cda5da8dSAndroid Build Coastguard Worker            if ssl_shutdown_timeout is not None:
247*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
248*cda5da8dSAndroid Build Coastguard Worker                    'ssl_shutdown_timeout is only meaningful with ssl')
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker        if path is not None:
251*cda5da8dSAndroid Build Coastguard Worker            if sock is not None:
252*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
253*cda5da8dSAndroid Build Coastguard Worker                    'path and sock can not be specified at the same time')
254*cda5da8dSAndroid Build Coastguard Worker
255*cda5da8dSAndroid Build Coastguard Worker            path = os.fspath(path)
256*cda5da8dSAndroid Build Coastguard Worker            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
257*cda5da8dSAndroid Build Coastguard Worker            try:
258*cda5da8dSAndroid Build Coastguard Worker                sock.setblocking(False)
259*cda5da8dSAndroid Build Coastguard Worker                await self.sock_connect(sock, path)
260*cda5da8dSAndroid Build Coastguard Worker            except:
261*cda5da8dSAndroid Build Coastguard Worker                sock.close()
262*cda5da8dSAndroid Build Coastguard Worker                raise
263*cda5da8dSAndroid Build Coastguard Worker
264*cda5da8dSAndroid Build Coastguard Worker        else:
265*cda5da8dSAndroid Build Coastguard Worker            if sock is None:
266*cda5da8dSAndroid Build Coastguard Worker                raise ValueError('no path and sock were specified')
267*cda5da8dSAndroid Build Coastguard Worker            if (sock.family != socket.AF_UNIX or
268*cda5da8dSAndroid Build Coastguard Worker                    sock.type != socket.SOCK_STREAM):
269*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
270*cda5da8dSAndroid Build Coastguard Worker                    f'A UNIX Domain Stream Socket was expected, got {sock!r}')
271*cda5da8dSAndroid Build Coastguard Worker            sock.setblocking(False)
272*cda5da8dSAndroid Build Coastguard Worker
273*cda5da8dSAndroid Build Coastguard Worker        transport, protocol = await self._create_connection_transport(
274*cda5da8dSAndroid Build Coastguard Worker            sock, protocol_factory, ssl, server_hostname,
275*cda5da8dSAndroid Build Coastguard Worker            ssl_handshake_timeout=ssl_handshake_timeout,
276*cda5da8dSAndroid Build Coastguard Worker            ssl_shutdown_timeout=ssl_shutdown_timeout)
277*cda5da8dSAndroid Build Coastguard Worker        return transport, protocol
278*cda5da8dSAndroid Build Coastguard Worker
279*cda5da8dSAndroid Build Coastguard Worker    async def create_unix_server(
280*cda5da8dSAndroid Build Coastguard Worker            self, protocol_factory, path=None, *,
281*cda5da8dSAndroid Build Coastguard Worker            sock=None, backlog=100, ssl=None,
282*cda5da8dSAndroid Build Coastguard Worker            ssl_handshake_timeout=None,
283*cda5da8dSAndroid Build Coastguard Worker            ssl_shutdown_timeout=None,
284*cda5da8dSAndroid Build Coastguard Worker            start_serving=True):
285*cda5da8dSAndroid Build Coastguard Worker        if isinstance(ssl, bool):
286*cda5da8dSAndroid Build Coastguard Worker            raise TypeError('ssl argument must be an SSLContext or None')
287*cda5da8dSAndroid Build Coastguard Worker
288*cda5da8dSAndroid Build Coastguard Worker        if ssl_handshake_timeout is not None and not ssl:
289*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
290*cda5da8dSAndroid Build Coastguard Worker                'ssl_handshake_timeout is only meaningful with ssl')
291*cda5da8dSAndroid Build Coastguard Worker
292*cda5da8dSAndroid Build Coastguard Worker        if ssl_shutdown_timeout is not None and not ssl:
293*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
294*cda5da8dSAndroid Build Coastguard Worker                'ssl_shutdown_timeout is only meaningful with ssl')
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker        if path is not None:
297*cda5da8dSAndroid Build Coastguard Worker            if sock is not None:
298*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
299*cda5da8dSAndroid Build Coastguard Worker                    'path and sock can not be specified at the same time')
300*cda5da8dSAndroid Build Coastguard Worker
301*cda5da8dSAndroid Build Coastguard Worker            path = os.fspath(path)
302*cda5da8dSAndroid Build Coastguard Worker            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
303*cda5da8dSAndroid Build Coastguard Worker
304*cda5da8dSAndroid Build Coastguard Worker            # Check for abstract socket. `str` and `bytes` paths are supported.
305*cda5da8dSAndroid Build Coastguard Worker            if path[0] not in (0, '\x00'):
306*cda5da8dSAndroid Build Coastguard Worker                try:
307*cda5da8dSAndroid Build Coastguard Worker                    if stat.S_ISSOCK(os.stat(path).st_mode):
308*cda5da8dSAndroid Build Coastguard Worker                        os.remove(path)
309*cda5da8dSAndroid Build Coastguard Worker                except FileNotFoundError:
310*cda5da8dSAndroid Build Coastguard Worker                    pass
311*cda5da8dSAndroid Build Coastguard Worker                except OSError as err:
312*cda5da8dSAndroid Build Coastguard Worker                    # Directory may have permissions only to create socket.
313*cda5da8dSAndroid Build Coastguard Worker                    logger.error('Unable to check or remove stale UNIX socket '
314*cda5da8dSAndroid Build Coastguard Worker                                 '%r: %r', path, err)
315*cda5da8dSAndroid Build Coastguard Worker
316*cda5da8dSAndroid Build Coastguard Worker            try:
317*cda5da8dSAndroid Build Coastguard Worker                sock.bind(path)
318*cda5da8dSAndroid Build Coastguard Worker            except OSError as exc:
319*cda5da8dSAndroid Build Coastguard Worker                sock.close()
320*cda5da8dSAndroid Build Coastguard Worker                if exc.errno == errno.EADDRINUSE:
321*cda5da8dSAndroid Build Coastguard Worker                    # Let's improve the error message by adding
322*cda5da8dSAndroid Build Coastguard Worker                    # with what exact address it occurs.
323*cda5da8dSAndroid Build Coastguard Worker                    msg = f'Address {path!r} is already in use'
324*cda5da8dSAndroid Build Coastguard Worker                    raise OSError(errno.EADDRINUSE, msg) from None
325*cda5da8dSAndroid Build Coastguard Worker                else:
326*cda5da8dSAndroid Build Coastguard Worker                    raise
327*cda5da8dSAndroid Build Coastguard Worker            except:
328*cda5da8dSAndroid Build Coastguard Worker                sock.close()
329*cda5da8dSAndroid Build Coastguard Worker                raise
330*cda5da8dSAndroid Build Coastguard Worker        else:
331*cda5da8dSAndroid Build Coastguard Worker            if sock is None:
332*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
333*cda5da8dSAndroid Build Coastguard Worker                    'path was not specified, and no sock specified')
334*cda5da8dSAndroid Build Coastguard Worker
335*cda5da8dSAndroid Build Coastguard Worker            if (sock.family != socket.AF_UNIX or
336*cda5da8dSAndroid Build Coastguard Worker                    sock.type != socket.SOCK_STREAM):
337*cda5da8dSAndroid Build Coastguard Worker                raise ValueError(
338*cda5da8dSAndroid Build Coastguard Worker                    f'A UNIX Domain Stream Socket was expected, got {sock!r}')
339*cda5da8dSAndroid Build Coastguard Worker
340*cda5da8dSAndroid Build Coastguard Worker        sock.setblocking(False)
341*cda5da8dSAndroid Build Coastguard Worker        server = base_events.Server(self, [sock], protocol_factory,
342*cda5da8dSAndroid Build Coastguard Worker                                    ssl, backlog, ssl_handshake_timeout,
343*cda5da8dSAndroid Build Coastguard Worker                                    ssl_shutdown_timeout)
344*cda5da8dSAndroid Build Coastguard Worker        if start_serving:
345*cda5da8dSAndroid Build Coastguard Worker            server._start_serving()
346*cda5da8dSAndroid Build Coastguard Worker            # Skip one loop iteration so that all 'loop.add_reader'
347*cda5da8dSAndroid Build Coastguard Worker            # go through.
348*cda5da8dSAndroid Build Coastguard Worker            await tasks.sleep(0)
349*cda5da8dSAndroid Build Coastguard Worker
350*cda5da8dSAndroid Build Coastguard Worker        return server
351*cda5da8dSAndroid Build Coastguard Worker
352*cda5da8dSAndroid Build Coastguard Worker    async def _sock_sendfile_native(self, sock, file, offset, count):
353*cda5da8dSAndroid Build Coastguard Worker        try:
354*cda5da8dSAndroid Build Coastguard Worker            os.sendfile
355*cda5da8dSAndroid Build Coastguard Worker        except AttributeError:
356*cda5da8dSAndroid Build Coastguard Worker            raise exceptions.SendfileNotAvailableError(
357*cda5da8dSAndroid Build Coastguard Worker                "os.sendfile() is not available")
358*cda5da8dSAndroid Build Coastguard Worker        try:
359*cda5da8dSAndroid Build Coastguard Worker            fileno = file.fileno()
360*cda5da8dSAndroid Build Coastguard Worker        except (AttributeError, io.UnsupportedOperation) as err:
361*cda5da8dSAndroid Build Coastguard Worker            raise exceptions.SendfileNotAvailableError("not a regular file")
362*cda5da8dSAndroid Build Coastguard Worker        try:
363*cda5da8dSAndroid Build Coastguard Worker            fsize = os.fstat(fileno).st_size
364*cda5da8dSAndroid Build Coastguard Worker        except OSError:
365*cda5da8dSAndroid Build Coastguard Worker            raise exceptions.SendfileNotAvailableError("not a regular file")
366*cda5da8dSAndroid Build Coastguard Worker        blocksize = count if count else fsize
367*cda5da8dSAndroid Build Coastguard Worker        if not blocksize:
368*cda5da8dSAndroid Build Coastguard Worker            return 0  # empty file
369*cda5da8dSAndroid Build Coastguard Worker
370*cda5da8dSAndroid Build Coastguard Worker        fut = self.create_future()
371*cda5da8dSAndroid Build Coastguard Worker        self._sock_sendfile_native_impl(fut, None, sock, fileno,
372*cda5da8dSAndroid Build Coastguard Worker                                        offset, count, blocksize, 0)
373*cda5da8dSAndroid Build Coastguard Worker        return await fut
374*cda5da8dSAndroid Build Coastguard Worker
375*cda5da8dSAndroid Build Coastguard Worker    def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
376*cda5da8dSAndroid Build Coastguard Worker                                   offset, count, blocksize, total_sent):
377*cda5da8dSAndroid Build Coastguard Worker        fd = sock.fileno()
378*cda5da8dSAndroid Build Coastguard Worker        if registered_fd is not None:
379*cda5da8dSAndroid Build Coastguard Worker            # Remove the callback early.  It should be rare that the
380*cda5da8dSAndroid Build Coastguard Worker            # selector says the fd is ready but the call still returns
381*cda5da8dSAndroid Build Coastguard Worker            # EAGAIN, and I am willing to take a hit in that case in
382*cda5da8dSAndroid Build Coastguard Worker            # order to simplify the common case.
383*cda5da8dSAndroid Build Coastguard Worker            self.remove_writer(registered_fd)
384*cda5da8dSAndroid Build Coastguard Worker        if fut.cancelled():
385*cda5da8dSAndroid Build Coastguard Worker            self._sock_sendfile_update_filepos(fileno, offset, total_sent)
386*cda5da8dSAndroid Build Coastguard Worker            return
387*cda5da8dSAndroid Build Coastguard Worker        if count:
388*cda5da8dSAndroid Build Coastguard Worker            blocksize = count - total_sent
389*cda5da8dSAndroid Build Coastguard Worker            if blocksize <= 0:
390*cda5da8dSAndroid Build Coastguard Worker                self._sock_sendfile_update_filepos(fileno, offset, total_sent)
391*cda5da8dSAndroid Build Coastguard Worker                fut.set_result(total_sent)
392*cda5da8dSAndroid Build Coastguard Worker                return
393*cda5da8dSAndroid Build Coastguard Worker
394*cda5da8dSAndroid Build Coastguard Worker        try:
395*cda5da8dSAndroid Build Coastguard Worker            sent = os.sendfile(fd, fileno, offset, blocksize)
396*cda5da8dSAndroid Build Coastguard Worker        except (BlockingIOError, InterruptedError):
397*cda5da8dSAndroid Build Coastguard Worker            if registered_fd is None:
398*cda5da8dSAndroid Build Coastguard Worker                self._sock_add_cancellation_callback(fut, sock)
399*cda5da8dSAndroid Build Coastguard Worker            self.add_writer(fd, self._sock_sendfile_native_impl, fut,
400*cda5da8dSAndroid Build Coastguard Worker                            fd, sock, fileno,
401*cda5da8dSAndroid Build Coastguard Worker                            offset, count, blocksize, total_sent)
402*cda5da8dSAndroid Build Coastguard Worker        except OSError as exc:
403*cda5da8dSAndroid Build Coastguard Worker            if (registered_fd is not None and
404*cda5da8dSAndroid Build Coastguard Worker                    exc.errno == errno.ENOTCONN and
405*cda5da8dSAndroid Build Coastguard Worker                    type(exc) is not ConnectionError):
406*cda5da8dSAndroid Build Coastguard Worker                # If we have an ENOTCONN and this isn't a first call to
407*cda5da8dSAndroid Build Coastguard Worker                # sendfile(), i.e. the connection was closed in the middle
408*cda5da8dSAndroid Build Coastguard Worker                # of the operation, normalize the error to ConnectionError
409*cda5da8dSAndroid Build Coastguard Worker                # to make it consistent across all Posix systems.
410*cda5da8dSAndroid Build Coastguard Worker                new_exc = ConnectionError(
411*cda5da8dSAndroid Build Coastguard Worker                    "socket is not connected", errno.ENOTCONN)
412*cda5da8dSAndroid Build Coastguard Worker                new_exc.__cause__ = exc
413*cda5da8dSAndroid Build Coastguard Worker                exc = new_exc
414*cda5da8dSAndroid Build Coastguard Worker            if total_sent == 0:
415*cda5da8dSAndroid Build Coastguard Worker                # We can get here for different reasons, the main
416*cda5da8dSAndroid Build Coastguard Worker                # one being 'file' is not a regular mmap(2)-like
417*cda5da8dSAndroid Build Coastguard Worker                # file, in which case we'll fall back on using
418*cda5da8dSAndroid Build Coastguard Worker                # plain send().
419*cda5da8dSAndroid Build Coastguard Worker                err = exceptions.SendfileNotAvailableError(
420*cda5da8dSAndroid Build Coastguard Worker                    "os.sendfile call failed")
421*cda5da8dSAndroid Build Coastguard Worker                self._sock_sendfile_update_filepos(fileno, offset, total_sent)
422*cda5da8dSAndroid Build Coastguard Worker                fut.set_exception(err)
423*cda5da8dSAndroid Build Coastguard Worker            else:
424*cda5da8dSAndroid Build Coastguard Worker                self._sock_sendfile_update_filepos(fileno, offset, total_sent)
425*cda5da8dSAndroid Build Coastguard Worker                fut.set_exception(exc)
426*cda5da8dSAndroid Build Coastguard Worker        except (SystemExit, KeyboardInterrupt):
427*cda5da8dSAndroid Build Coastguard Worker            raise
428*cda5da8dSAndroid Build Coastguard Worker        except BaseException as exc:
429*cda5da8dSAndroid Build Coastguard Worker            self._sock_sendfile_update_filepos(fileno, offset, total_sent)
430*cda5da8dSAndroid Build Coastguard Worker            fut.set_exception(exc)
431*cda5da8dSAndroid Build Coastguard Worker        else:
432*cda5da8dSAndroid Build Coastguard Worker            if sent == 0:
433*cda5da8dSAndroid Build Coastguard Worker                # EOF
434*cda5da8dSAndroid Build Coastguard Worker                self._sock_sendfile_update_filepos(fileno, offset, total_sent)
435*cda5da8dSAndroid Build Coastguard Worker                fut.set_result(total_sent)
436*cda5da8dSAndroid Build Coastguard Worker            else:
437*cda5da8dSAndroid Build Coastguard Worker                offset += sent
438*cda5da8dSAndroid Build Coastguard Worker                total_sent += sent
439*cda5da8dSAndroid Build Coastguard Worker                if registered_fd is None:
440*cda5da8dSAndroid Build Coastguard Worker                    self._sock_add_cancellation_callback(fut, sock)
441*cda5da8dSAndroid Build Coastguard Worker                self.add_writer(fd, self._sock_sendfile_native_impl, fut,
442*cda5da8dSAndroid Build Coastguard Worker                                fd, sock, fileno,
443*cda5da8dSAndroid Build Coastguard Worker                                offset, count, blocksize, total_sent)
444*cda5da8dSAndroid Build Coastguard Worker
445*cda5da8dSAndroid Build Coastguard Worker    def _sock_sendfile_update_filepos(self, fileno, offset, total_sent):
446*cda5da8dSAndroid Build Coastguard Worker        if total_sent > 0:
447*cda5da8dSAndroid Build Coastguard Worker            os.lseek(fileno, offset, os.SEEK_SET)
448*cda5da8dSAndroid Build Coastguard Worker
449*cda5da8dSAndroid Build Coastguard Worker    def _sock_add_cancellation_callback(self, fut, sock):
450*cda5da8dSAndroid Build Coastguard Worker        def cb(fut):
451*cda5da8dSAndroid Build Coastguard Worker            if fut.cancelled():
452*cda5da8dSAndroid Build Coastguard Worker                fd = sock.fileno()
453*cda5da8dSAndroid Build Coastguard Worker                if fd != -1:
454*cda5da8dSAndroid Build Coastguard Worker                    self.remove_writer(fd)
455*cda5da8dSAndroid Build Coastguard Worker        fut.add_done_callback(cb)
456*cda5da8dSAndroid Build Coastguard Worker
457*cda5da8dSAndroid Build Coastguard Worker
458*cda5da8dSAndroid Build Coastguard Workerclass _UnixReadPipeTransport(transports.ReadTransport):
459*cda5da8dSAndroid Build Coastguard Worker
460*cda5da8dSAndroid Build Coastguard Worker    max_size = 256 * 1024  # max bytes we read in one event loop iteration
461*cda5da8dSAndroid Build Coastguard Worker
462*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
463*cda5da8dSAndroid Build Coastguard Worker        super().__init__(extra)
464*cda5da8dSAndroid Build Coastguard Worker        self._extra['pipe'] = pipe
465*cda5da8dSAndroid Build Coastguard Worker        self._loop = loop
466*cda5da8dSAndroid Build Coastguard Worker        self._pipe = pipe
467*cda5da8dSAndroid Build Coastguard Worker        self._fileno = pipe.fileno()
468*cda5da8dSAndroid Build Coastguard Worker        self._protocol = protocol
469*cda5da8dSAndroid Build Coastguard Worker        self._closing = False
470*cda5da8dSAndroid Build Coastguard Worker        self._paused = False
471*cda5da8dSAndroid Build Coastguard Worker
472*cda5da8dSAndroid Build Coastguard Worker        mode = os.fstat(self._fileno).st_mode
473*cda5da8dSAndroid Build Coastguard Worker        if not (stat.S_ISFIFO(mode) or
474*cda5da8dSAndroid Build Coastguard Worker                stat.S_ISSOCK(mode) or
475*cda5da8dSAndroid Build Coastguard Worker                stat.S_ISCHR(mode)):
476*cda5da8dSAndroid Build Coastguard Worker            self._pipe = None
477*cda5da8dSAndroid Build Coastguard Worker            self._fileno = None
478*cda5da8dSAndroid Build Coastguard Worker            self._protocol = None
479*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Pipe transport is for pipes/sockets only.")
480*cda5da8dSAndroid Build Coastguard Worker
481*cda5da8dSAndroid Build Coastguard Worker        os.set_blocking(self._fileno, False)
482*cda5da8dSAndroid Build Coastguard Worker
483*cda5da8dSAndroid Build Coastguard Worker        self._loop.call_soon(self._protocol.connection_made, self)
484*cda5da8dSAndroid Build Coastguard Worker        # only start reading when connection_made() has been called
485*cda5da8dSAndroid Build Coastguard Worker        self._loop.call_soon(self._add_reader,
486*cda5da8dSAndroid Build Coastguard Worker                             self._fileno, self._read_ready)
487*cda5da8dSAndroid Build Coastguard Worker        if waiter is not None:
488*cda5da8dSAndroid Build Coastguard Worker            # only wake up the waiter when connection_made() has been called
489*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_soon(futures._set_result_unless_cancelled,
490*cda5da8dSAndroid Build Coastguard Worker                                 waiter, None)
491*cda5da8dSAndroid Build Coastguard Worker
492*cda5da8dSAndroid Build Coastguard Worker    def _add_reader(self, fd, callback):
493*cda5da8dSAndroid Build Coastguard Worker        if not self.is_reading():
494*cda5da8dSAndroid Build Coastguard Worker            return
495*cda5da8dSAndroid Build Coastguard Worker        self._loop._add_reader(fd, callback)
496*cda5da8dSAndroid Build Coastguard Worker
497*cda5da8dSAndroid Build Coastguard Worker    def is_reading(self):
498*cda5da8dSAndroid Build Coastguard Worker        return not self._paused and not self._closing
499*cda5da8dSAndroid Build Coastguard Worker
500*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
501*cda5da8dSAndroid Build Coastguard Worker        info = [self.__class__.__name__]
502*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is None:
503*cda5da8dSAndroid Build Coastguard Worker            info.append('closed')
504*cda5da8dSAndroid Build Coastguard Worker        elif self._closing:
505*cda5da8dSAndroid Build Coastguard Worker            info.append('closing')
506*cda5da8dSAndroid Build Coastguard Worker        info.append(f'fd={self._fileno}')
507*cda5da8dSAndroid Build Coastguard Worker        selector = getattr(self._loop, '_selector', None)
508*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is not None and selector is not None:
509*cda5da8dSAndroid Build Coastguard Worker            polling = selector_events._test_selector_event(
510*cda5da8dSAndroid Build Coastguard Worker                selector, self._fileno, selectors.EVENT_READ)
511*cda5da8dSAndroid Build Coastguard Worker            if polling:
512*cda5da8dSAndroid Build Coastguard Worker                info.append('polling')
513*cda5da8dSAndroid Build Coastguard Worker            else:
514*cda5da8dSAndroid Build Coastguard Worker                info.append('idle')
515*cda5da8dSAndroid Build Coastguard Worker        elif self._pipe is not None:
516*cda5da8dSAndroid Build Coastguard Worker            info.append('open')
517*cda5da8dSAndroid Build Coastguard Worker        else:
518*cda5da8dSAndroid Build Coastguard Worker            info.append('closed')
519*cda5da8dSAndroid Build Coastguard Worker        return '<{}>'.format(' '.join(info))
520*cda5da8dSAndroid Build Coastguard Worker
521*cda5da8dSAndroid Build Coastguard Worker    def _read_ready(self):
522*cda5da8dSAndroid Build Coastguard Worker        try:
523*cda5da8dSAndroid Build Coastguard Worker            data = os.read(self._fileno, self.max_size)
524*cda5da8dSAndroid Build Coastguard Worker        except (BlockingIOError, InterruptedError):
525*cda5da8dSAndroid Build Coastguard Worker            pass
526*cda5da8dSAndroid Build Coastguard Worker        except OSError as exc:
527*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(exc, 'Fatal read error on pipe transport')
528*cda5da8dSAndroid Build Coastguard Worker        else:
529*cda5da8dSAndroid Build Coastguard Worker            if data:
530*cda5da8dSAndroid Build Coastguard Worker                self._protocol.data_received(data)
531*cda5da8dSAndroid Build Coastguard Worker            else:
532*cda5da8dSAndroid Build Coastguard Worker                if self._loop.get_debug():
533*cda5da8dSAndroid Build Coastguard Worker                    logger.info("%r was closed by peer", self)
534*cda5da8dSAndroid Build Coastguard Worker                self._closing = True
535*cda5da8dSAndroid Build Coastguard Worker                self._loop._remove_reader(self._fileno)
536*cda5da8dSAndroid Build Coastguard Worker                self._loop.call_soon(self._protocol.eof_received)
537*cda5da8dSAndroid Build Coastguard Worker                self._loop.call_soon(self._call_connection_lost, None)
538*cda5da8dSAndroid Build Coastguard Worker
539*cda5da8dSAndroid Build Coastguard Worker    def pause_reading(self):
540*cda5da8dSAndroid Build Coastguard Worker        if not self.is_reading():
541*cda5da8dSAndroid Build Coastguard Worker            return
542*cda5da8dSAndroid Build Coastguard Worker        self._paused = True
543*cda5da8dSAndroid Build Coastguard Worker        self._loop._remove_reader(self._fileno)
544*cda5da8dSAndroid Build Coastguard Worker        if self._loop.get_debug():
545*cda5da8dSAndroid Build Coastguard Worker            logger.debug("%r pauses reading", self)
546*cda5da8dSAndroid Build Coastguard Worker
547*cda5da8dSAndroid Build Coastguard Worker    def resume_reading(self):
548*cda5da8dSAndroid Build Coastguard Worker        if self._closing or not self._paused:
549*cda5da8dSAndroid Build Coastguard Worker            return
550*cda5da8dSAndroid Build Coastguard Worker        self._paused = False
551*cda5da8dSAndroid Build Coastguard Worker        self._loop._add_reader(self._fileno, self._read_ready)
552*cda5da8dSAndroid Build Coastguard Worker        if self._loop.get_debug():
553*cda5da8dSAndroid Build Coastguard Worker            logger.debug("%r resumes reading", self)
554*cda5da8dSAndroid Build Coastguard Worker
555*cda5da8dSAndroid Build Coastguard Worker    def set_protocol(self, protocol):
556*cda5da8dSAndroid Build Coastguard Worker        self._protocol = protocol
557*cda5da8dSAndroid Build Coastguard Worker
558*cda5da8dSAndroid Build Coastguard Worker    def get_protocol(self):
559*cda5da8dSAndroid Build Coastguard Worker        return self._protocol
560*cda5da8dSAndroid Build Coastguard Worker
561*cda5da8dSAndroid Build Coastguard Worker    def is_closing(self):
562*cda5da8dSAndroid Build Coastguard Worker        return self._closing
563*cda5da8dSAndroid Build Coastguard Worker
564*cda5da8dSAndroid Build Coastguard Worker    def close(self):
565*cda5da8dSAndroid Build Coastguard Worker        if not self._closing:
566*cda5da8dSAndroid Build Coastguard Worker            self._close(None)
567*cda5da8dSAndroid Build Coastguard Worker
568*cda5da8dSAndroid Build Coastguard Worker    def __del__(self, _warn=warnings.warn):
569*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is not None:
570*cda5da8dSAndroid Build Coastguard Worker            _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
571*cda5da8dSAndroid Build Coastguard Worker            self._pipe.close()
572*cda5da8dSAndroid Build Coastguard Worker
573*cda5da8dSAndroid Build Coastguard Worker    def _fatal_error(self, exc, message='Fatal error on pipe transport'):
574*cda5da8dSAndroid Build Coastguard Worker        # should be called by exception handler only
575*cda5da8dSAndroid Build Coastguard Worker        if (isinstance(exc, OSError) and exc.errno == errno.EIO):
576*cda5da8dSAndroid Build Coastguard Worker            if self._loop.get_debug():
577*cda5da8dSAndroid Build Coastguard Worker                logger.debug("%r: %s", self, message, exc_info=True)
578*cda5da8dSAndroid Build Coastguard Worker        else:
579*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_exception_handler({
580*cda5da8dSAndroid Build Coastguard Worker                'message': message,
581*cda5da8dSAndroid Build Coastguard Worker                'exception': exc,
582*cda5da8dSAndroid Build Coastguard Worker                'transport': self,
583*cda5da8dSAndroid Build Coastguard Worker                'protocol': self._protocol,
584*cda5da8dSAndroid Build Coastguard Worker            })
585*cda5da8dSAndroid Build Coastguard Worker        self._close(exc)
586*cda5da8dSAndroid Build Coastguard Worker
587*cda5da8dSAndroid Build Coastguard Worker    def _close(self, exc):
588*cda5da8dSAndroid Build Coastguard Worker        self._closing = True
589*cda5da8dSAndroid Build Coastguard Worker        self._loop._remove_reader(self._fileno)
590*cda5da8dSAndroid Build Coastguard Worker        self._loop.call_soon(self._call_connection_lost, exc)
591*cda5da8dSAndroid Build Coastguard Worker
592*cda5da8dSAndroid Build Coastguard Worker    def _call_connection_lost(self, exc):
593*cda5da8dSAndroid Build Coastguard Worker        try:
594*cda5da8dSAndroid Build Coastguard Worker            self._protocol.connection_lost(exc)
595*cda5da8dSAndroid Build Coastguard Worker        finally:
596*cda5da8dSAndroid Build Coastguard Worker            self._pipe.close()
597*cda5da8dSAndroid Build Coastguard Worker            self._pipe = None
598*cda5da8dSAndroid Build Coastguard Worker            self._protocol = None
599*cda5da8dSAndroid Build Coastguard Worker            self._loop = None
600*cda5da8dSAndroid Build Coastguard Worker
601*cda5da8dSAndroid Build Coastguard Worker
602*cda5da8dSAndroid Build Coastguard Workerclass _UnixWritePipeTransport(transports._FlowControlMixin,
603*cda5da8dSAndroid Build Coastguard Worker                              transports.WriteTransport):
604*cda5da8dSAndroid Build Coastguard Worker
605*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
606*cda5da8dSAndroid Build Coastguard Worker        super().__init__(extra, loop)
607*cda5da8dSAndroid Build Coastguard Worker        self._extra['pipe'] = pipe
608*cda5da8dSAndroid Build Coastguard Worker        self._pipe = pipe
609*cda5da8dSAndroid Build Coastguard Worker        self._fileno = pipe.fileno()
610*cda5da8dSAndroid Build Coastguard Worker        self._protocol = protocol
611*cda5da8dSAndroid Build Coastguard Worker        self._buffer = bytearray()
612*cda5da8dSAndroid Build Coastguard Worker        self._conn_lost = 0
613*cda5da8dSAndroid Build Coastguard Worker        self._closing = False  # Set when close() or write_eof() called.
614*cda5da8dSAndroid Build Coastguard Worker
615*cda5da8dSAndroid Build Coastguard Worker        mode = os.fstat(self._fileno).st_mode
616*cda5da8dSAndroid Build Coastguard Worker        is_char = stat.S_ISCHR(mode)
617*cda5da8dSAndroid Build Coastguard Worker        is_fifo = stat.S_ISFIFO(mode)
618*cda5da8dSAndroid Build Coastguard Worker        is_socket = stat.S_ISSOCK(mode)
619*cda5da8dSAndroid Build Coastguard Worker        if not (is_char or is_fifo or is_socket):
620*cda5da8dSAndroid Build Coastguard Worker            self._pipe = None
621*cda5da8dSAndroid Build Coastguard Worker            self._fileno = None
622*cda5da8dSAndroid Build Coastguard Worker            self._protocol = None
623*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Pipe transport is only for "
624*cda5da8dSAndroid Build Coastguard Worker                             "pipes, sockets and character devices")
625*cda5da8dSAndroid Build Coastguard Worker
626*cda5da8dSAndroid Build Coastguard Worker        os.set_blocking(self._fileno, False)
627*cda5da8dSAndroid Build Coastguard Worker        self._loop.call_soon(self._protocol.connection_made, self)
628*cda5da8dSAndroid Build Coastguard Worker
629*cda5da8dSAndroid Build Coastguard Worker        # On AIX, the reader trick (to be notified when the read end of the
630*cda5da8dSAndroid Build Coastguard Worker        # socket is closed) only works for sockets. On other platforms it
631*cda5da8dSAndroid Build Coastguard Worker        # works for pipes and sockets. (Exception: OS X 10.4?  Issue #19294.)
632*cda5da8dSAndroid Build Coastguard Worker        if is_socket or (is_fifo and not sys.platform.startswith("aix")):
633*cda5da8dSAndroid Build Coastguard Worker            # only start reading when connection_made() has been called
634*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_soon(self._loop._add_reader,
635*cda5da8dSAndroid Build Coastguard Worker                                 self._fileno, self._read_ready)
636*cda5da8dSAndroid Build Coastguard Worker
637*cda5da8dSAndroid Build Coastguard Worker        if waiter is not None:
638*cda5da8dSAndroid Build Coastguard Worker            # only wake up the waiter when connection_made() has been called
639*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_soon(futures._set_result_unless_cancelled,
640*cda5da8dSAndroid Build Coastguard Worker                                 waiter, None)
641*cda5da8dSAndroid Build Coastguard Worker
642*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
643*cda5da8dSAndroid Build Coastguard Worker        info = [self.__class__.__name__]
644*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is None:
645*cda5da8dSAndroid Build Coastguard Worker            info.append('closed')
646*cda5da8dSAndroid Build Coastguard Worker        elif self._closing:
647*cda5da8dSAndroid Build Coastguard Worker            info.append('closing')
648*cda5da8dSAndroid Build Coastguard Worker        info.append(f'fd={self._fileno}')
649*cda5da8dSAndroid Build Coastguard Worker        selector = getattr(self._loop, '_selector', None)
650*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is not None and selector is not None:
651*cda5da8dSAndroid Build Coastguard Worker            polling = selector_events._test_selector_event(
652*cda5da8dSAndroid Build Coastguard Worker                selector, self._fileno, selectors.EVENT_WRITE)
653*cda5da8dSAndroid Build Coastguard Worker            if polling:
654*cda5da8dSAndroid Build Coastguard Worker                info.append('polling')
655*cda5da8dSAndroid Build Coastguard Worker            else:
656*cda5da8dSAndroid Build Coastguard Worker                info.append('idle')
657*cda5da8dSAndroid Build Coastguard Worker
658*cda5da8dSAndroid Build Coastguard Worker            bufsize = self.get_write_buffer_size()
659*cda5da8dSAndroid Build Coastguard Worker            info.append(f'bufsize={bufsize}')
660*cda5da8dSAndroid Build Coastguard Worker        elif self._pipe is not None:
661*cda5da8dSAndroid Build Coastguard Worker            info.append('open')
662*cda5da8dSAndroid Build Coastguard Worker        else:
663*cda5da8dSAndroid Build Coastguard Worker            info.append('closed')
664*cda5da8dSAndroid Build Coastguard Worker        return '<{}>'.format(' '.join(info))
665*cda5da8dSAndroid Build Coastguard Worker
666*cda5da8dSAndroid Build Coastguard Worker    def get_write_buffer_size(self):
667*cda5da8dSAndroid Build Coastguard Worker        return len(self._buffer)
668*cda5da8dSAndroid Build Coastguard Worker
669*cda5da8dSAndroid Build Coastguard Worker    def _read_ready(self):
670*cda5da8dSAndroid Build Coastguard Worker        # Pipe was closed by peer.
671*cda5da8dSAndroid Build Coastguard Worker        if self._loop.get_debug():
672*cda5da8dSAndroid Build Coastguard Worker            logger.info("%r was closed by peer", self)
673*cda5da8dSAndroid Build Coastguard Worker        if self._buffer:
674*cda5da8dSAndroid Build Coastguard Worker            self._close(BrokenPipeError())
675*cda5da8dSAndroid Build Coastguard Worker        else:
676*cda5da8dSAndroid Build Coastguard Worker            self._close()
677*cda5da8dSAndroid Build Coastguard Worker
678*cda5da8dSAndroid Build Coastguard Worker    def write(self, data):
679*cda5da8dSAndroid Build Coastguard Worker        assert isinstance(data, (bytes, bytearray, memoryview)), repr(data)
680*cda5da8dSAndroid Build Coastguard Worker        if isinstance(data, bytearray):
681*cda5da8dSAndroid Build Coastguard Worker            data = memoryview(data)
682*cda5da8dSAndroid Build Coastguard Worker        if not data:
683*cda5da8dSAndroid Build Coastguard Worker            return
684*cda5da8dSAndroid Build Coastguard Worker
685*cda5da8dSAndroid Build Coastguard Worker        if self._conn_lost or self._closing:
686*cda5da8dSAndroid Build Coastguard Worker            if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
687*cda5da8dSAndroid Build Coastguard Worker                logger.warning('pipe closed by peer or '
688*cda5da8dSAndroid Build Coastguard Worker                               'os.write(pipe, data) raised exception.')
689*cda5da8dSAndroid Build Coastguard Worker            self._conn_lost += 1
690*cda5da8dSAndroid Build Coastguard Worker            return
691*cda5da8dSAndroid Build Coastguard Worker
692*cda5da8dSAndroid Build Coastguard Worker        if not self._buffer:
693*cda5da8dSAndroid Build Coastguard Worker            # Attempt to send it right away first.
694*cda5da8dSAndroid Build Coastguard Worker            try:
695*cda5da8dSAndroid Build Coastguard Worker                n = os.write(self._fileno, data)
696*cda5da8dSAndroid Build Coastguard Worker            except (BlockingIOError, InterruptedError):
697*cda5da8dSAndroid Build Coastguard Worker                n = 0
698*cda5da8dSAndroid Build Coastguard Worker            except (SystemExit, KeyboardInterrupt):
699*cda5da8dSAndroid Build Coastguard Worker                raise
700*cda5da8dSAndroid Build Coastguard Worker            except BaseException as exc:
701*cda5da8dSAndroid Build Coastguard Worker                self._conn_lost += 1
702*cda5da8dSAndroid Build Coastguard Worker                self._fatal_error(exc, 'Fatal write error on pipe transport')
703*cda5da8dSAndroid Build Coastguard Worker                return
704*cda5da8dSAndroid Build Coastguard Worker            if n == len(data):
705*cda5da8dSAndroid Build Coastguard Worker                return
706*cda5da8dSAndroid Build Coastguard Worker            elif n > 0:
707*cda5da8dSAndroid Build Coastguard Worker                data = memoryview(data)[n:]
708*cda5da8dSAndroid Build Coastguard Worker            self._loop._add_writer(self._fileno, self._write_ready)
709*cda5da8dSAndroid Build Coastguard Worker
710*cda5da8dSAndroid Build Coastguard Worker        self._buffer += data
711*cda5da8dSAndroid Build Coastguard Worker        self._maybe_pause_protocol()
712*cda5da8dSAndroid Build Coastguard Worker
713*cda5da8dSAndroid Build Coastguard Worker    def _write_ready(self):
714*cda5da8dSAndroid Build Coastguard Worker        assert self._buffer, 'Data should not be empty'
715*cda5da8dSAndroid Build Coastguard Worker
716*cda5da8dSAndroid Build Coastguard Worker        try:
717*cda5da8dSAndroid Build Coastguard Worker            n = os.write(self._fileno, self._buffer)
718*cda5da8dSAndroid Build Coastguard Worker        except (BlockingIOError, InterruptedError):
719*cda5da8dSAndroid Build Coastguard Worker            pass
720*cda5da8dSAndroid Build Coastguard Worker        except (SystemExit, KeyboardInterrupt):
721*cda5da8dSAndroid Build Coastguard Worker            raise
722*cda5da8dSAndroid Build Coastguard Worker        except BaseException as exc:
723*cda5da8dSAndroid Build Coastguard Worker            self._buffer.clear()
724*cda5da8dSAndroid Build Coastguard Worker            self._conn_lost += 1
725*cda5da8dSAndroid Build Coastguard Worker            # Remove writer here, _fatal_error() doesn't it
726*cda5da8dSAndroid Build Coastguard Worker            # because _buffer is empty.
727*cda5da8dSAndroid Build Coastguard Worker            self._loop._remove_writer(self._fileno)
728*cda5da8dSAndroid Build Coastguard Worker            self._fatal_error(exc, 'Fatal write error on pipe transport')
729*cda5da8dSAndroid Build Coastguard Worker        else:
730*cda5da8dSAndroid Build Coastguard Worker            if n == len(self._buffer):
731*cda5da8dSAndroid Build Coastguard Worker                self._buffer.clear()
732*cda5da8dSAndroid Build Coastguard Worker                self._loop._remove_writer(self._fileno)
733*cda5da8dSAndroid Build Coastguard Worker                self._maybe_resume_protocol()  # May append to buffer.
734*cda5da8dSAndroid Build Coastguard Worker                if self._closing:
735*cda5da8dSAndroid Build Coastguard Worker                    self._loop._remove_reader(self._fileno)
736*cda5da8dSAndroid Build Coastguard Worker                    self._call_connection_lost(None)
737*cda5da8dSAndroid Build Coastguard Worker                return
738*cda5da8dSAndroid Build Coastguard Worker            elif n > 0:
739*cda5da8dSAndroid Build Coastguard Worker                del self._buffer[:n]
740*cda5da8dSAndroid Build Coastguard Worker
741*cda5da8dSAndroid Build Coastguard Worker    def can_write_eof(self):
742*cda5da8dSAndroid Build Coastguard Worker        return True
743*cda5da8dSAndroid Build Coastguard Worker
744*cda5da8dSAndroid Build Coastguard Worker    def write_eof(self):
745*cda5da8dSAndroid Build Coastguard Worker        if self._closing:
746*cda5da8dSAndroid Build Coastguard Worker            return
747*cda5da8dSAndroid Build Coastguard Worker        assert self._pipe
748*cda5da8dSAndroid Build Coastguard Worker        self._closing = True
749*cda5da8dSAndroid Build Coastguard Worker        if not self._buffer:
750*cda5da8dSAndroid Build Coastguard Worker            self._loop._remove_reader(self._fileno)
751*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_soon(self._call_connection_lost, None)
752*cda5da8dSAndroid Build Coastguard Worker
753*cda5da8dSAndroid Build Coastguard Worker    def set_protocol(self, protocol):
754*cda5da8dSAndroid Build Coastguard Worker        self._protocol = protocol
755*cda5da8dSAndroid Build Coastguard Worker
756*cda5da8dSAndroid Build Coastguard Worker    def get_protocol(self):
757*cda5da8dSAndroid Build Coastguard Worker        return self._protocol
758*cda5da8dSAndroid Build Coastguard Worker
759*cda5da8dSAndroid Build Coastguard Worker    def is_closing(self):
760*cda5da8dSAndroid Build Coastguard Worker        return self._closing
761*cda5da8dSAndroid Build Coastguard Worker
762*cda5da8dSAndroid Build Coastguard Worker    def close(self):
763*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is not None and not self._closing:
764*cda5da8dSAndroid Build Coastguard Worker            # write_eof is all what we needed to close the write pipe
765*cda5da8dSAndroid Build Coastguard Worker            self.write_eof()
766*cda5da8dSAndroid Build Coastguard Worker
767*cda5da8dSAndroid Build Coastguard Worker    def __del__(self, _warn=warnings.warn):
768*cda5da8dSAndroid Build Coastguard Worker        if self._pipe is not None:
769*cda5da8dSAndroid Build Coastguard Worker            _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
770*cda5da8dSAndroid Build Coastguard Worker            self._pipe.close()
771*cda5da8dSAndroid Build Coastguard Worker
772*cda5da8dSAndroid Build Coastguard Worker    def abort(self):
773*cda5da8dSAndroid Build Coastguard Worker        self._close(None)
774*cda5da8dSAndroid Build Coastguard Worker
775*cda5da8dSAndroid Build Coastguard Worker    def _fatal_error(self, exc, message='Fatal error on pipe transport'):
776*cda5da8dSAndroid Build Coastguard Worker        # should be called by exception handler only
777*cda5da8dSAndroid Build Coastguard Worker        if isinstance(exc, OSError):
778*cda5da8dSAndroid Build Coastguard Worker            if self._loop.get_debug():
779*cda5da8dSAndroid Build Coastguard Worker                logger.debug("%r: %s", self, message, exc_info=True)
780*cda5da8dSAndroid Build Coastguard Worker        else:
781*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_exception_handler({
782*cda5da8dSAndroid Build Coastguard Worker                'message': message,
783*cda5da8dSAndroid Build Coastguard Worker                'exception': exc,
784*cda5da8dSAndroid Build Coastguard Worker                'transport': self,
785*cda5da8dSAndroid Build Coastguard Worker                'protocol': self._protocol,
786*cda5da8dSAndroid Build Coastguard Worker            })
787*cda5da8dSAndroid Build Coastguard Worker        self._close(exc)
788*cda5da8dSAndroid Build Coastguard Worker
789*cda5da8dSAndroid Build Coastguard Worker    def _close(self, exc=None):
790*cda5da8dSAndroid Build Coastguard Worker        self._closing = True
791*cda5da8dSAndroid Build Coastguard Worker        if self._buffer:
792*cda5da8dSAndroid Build Coastguard Worker            self._loop._remove_writer(self._fileno)
793*cda5da8dSAndroid Build Coastguard Worker        self._buffer.clear()
794*cda5da8dSAndroid Build Coastguard Worker        self._loop._remove_reader(self._fileno)
795*cda5da8dSAndroid Build Coastguard Worker        self._loop.call_soon(self._call_connection_lost, exc)
796*cda5da8dSAndroid Build Coastguard Worker
797*cda5da8dSAndroid Build Coastguard Worker    def _call_connection_lost(self, exc):
798*cda5da8dSAndroid Build Coastguard Worker        try:
799*cda5da8dSAndroid Build Coastguard Worker            self._protocol.connection_lost(exc)
800*cda5da8dSAndroid Build Coastguard Worker        finally:
801*cda5da8dSAndroid Build Coastguard Worker            self._pipe.close()
802*cda5da8dSAndroid Build Coastguard Worker            self._pipe = None
803*cda5da8dSAndroid Build Coastguard Worker            self._protocol = None
804*cda5da8dSAndroid Build Coastguard Worker            self._loop = None
805*cda5da8dSAndroid Build Coastguard Worker
806*cda5da8dSAndroid Build Coastguard Worker
807*cda5da8dSAndroid Build Coastguard Workerclass _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport):
808*cda5da8dSAndroid Build Coastguard Worker
809*cda5da8dSAndroid Build Coastguard Worker    def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
810*cda5da8dSAndroid Build Coastguard Worker        stdin_w = None
811*cda5da8dSAndroid Build Coastguard Worker        if stdin == subprocess.PIPE and sys.platform.startswith('aix'):
812*cda5da8dSAndroid Build Coastguard Worker            # Use a socket pair for stdin on AIX, since it does not
813*cda5da8dSAndroid Build Coastguard Worker            # support selecting read events on the write end of a
814*cda5da8dSAndroid Build Coastguard Worker            # socket (which we use in order to detect closing of the
815*cda5da8dSAndroid Build Coastguard Worker            # other end).
816*cda5da8dSAndroid Build Coastguard Worker            stdin, stdin_w = socket.socketpair()
817*cda5da8dSAndroid Build Coastguard Worker        try:
818*cda5da8dSAndroid Build Coastguard Worker            self._proc = subprocess.Popen(
819*cda5da8dSAndroid Build Coastguard Worker                args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
820*cda5da8dSAndroid Build Coastguard Worker                universal_newlines=False, bufsize=bufsize, **kwargs)
821*cda5da8dSAndroid Build Coastguard Worker            if stdin_w is not None:
822*cda5da8dSAndroid Build Coastguard Worker                stdin.close()
823*cda5da8dSAndroid Build Coastguard Worker                self._proc.stdin = open(stdin_w.detach(), 'wb', buffering=bufsize)
824*cda5da8dSAndroid Build Coastguard Worker                stdin_w = None
825*cda5da8dSAndroid Build Coastguard Worker        finally:
826*cda5da8dSAndroid Build Coastguard Worker            if stdin_w is not None:
827*cda5da8dSAndroid Build Coastguard Worker                stdin.close()
828*cda5da8dSAndroid Build Coastguard Worker                stdin_w.close()
829*cda5da8dSAndroid Build Coastguard Worker
830*cda5da8dSAndroid Build Coastguard Worker
831*cda5da8dSAndroid Build Coastguard Workerclass AbstractChildWatcher:
832*cda5da8dSAndroid Build Coastguard Worker    """Abstract base class for monitoring child processes.
833*cda5da8dSAndroid Build Coastguard Worker
834*cda5da8dSAndroid Build Coastguard Worker    Objects derived from this class monitor a collection of subprocesses and
835*cda5da8dSAndroid Build Coastguard Worker    report their termination or interruption by a signal.
836*cda5da8dSAndroid Build Coastguard Worker
837*cda5da8dSAndroid Build Coastguard Worker    New callbacks are registered with .add_child_handler(). Starting a new
838*cda5da8dSAndroid Build Coastguard Worker    process must be done within a 'with' block to allow the watcher to suspend
839*cda5da8dSAndroid Build Coastguard Worker    its activity until the new process if fully registered (this is needed to
840*cda5da8dSAndroid Build Coastguard Worker    prevent a race condition in some implementations).
841*cda5da8dSAndroid Build Coastguard Worker
842*cda5da8dSAndroid Build Coastguard Worker    Example:
843*cda5da8dSAndroid Build Coastguard Worker        with watcher:
844*cda5da8dSAndroid Build Coastguard Worker            proc = subprocess.Popen("sleep 1")
845*cda5da8dSAndroid Build Coastguard Worker            watcher.add_child_handler(proc.pid, callback)
846*cda5da8dSAndroid Build Coastguard Worker
847*cda5da8dSAndroid Build Coastguard Worker    Notes:
848*cda5da8dSAndroid Build Coastguard Worker        Implementations of this class must be thread-safe.
849*cda5da8dSAndroid Build Coastguard Worker
850*cda5da8dSAndroid Build Coastguard Worker        Since child watcher objects may catch the SIGCHLD signal and call
851*cda5da8dSAndroid Build Coastguard Worker        waitpid(-1), there should be only one active object per process.
852*cda5da8dSAndroid Build Coastguard Worker    """
853*cda5da8dSAndroid Build Coastguard Worker
854*cda5da8dSAndroid Build Coastguard Worker    def add_child_handler(self, pid, callback, *args):
855*cda5da8dSAndroid Build Coastguard Worker        """Register a new child handler.
856*cda5da8dSAndroid Build Coastguard Worker
857*cda5da8dSAndroid Build Coastguard Worker        Arrange for callback(pid, returncode, *args) to be called when
858*cda5da8dSAndroid Build Coastguard Worker        process 'pid' terminates. Specifying another callback for the same
859*cda5da8dSAndroid Build Coastguard Worker        process replaces the previous handler.
860*cda5da8dSAndroid Build Coastguard Worker
861*cda5da8dSAndroid Build Coastguard Worker        Note: callback() must be thread-safe.
862*cda5da8dSAndroid Build Coastguard Worker        """
863*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
864*cda5da8dSAndroid Build Coastguard Worker
865*cda5da8dSAndroid Build Coastguard Worker    def remove_child_handler(self, pid):
866*cda5da8dSAndroid Build Coastguard Worker        """Removes the handler for process 'pid'.
867*cda5da8dSAndroid Build Coastguard Worker
868*cda5da8dSAndroid Build Coastguard Worker        The function returns True if the handler was successfully removed,
869*cda5da8dSAndroid Build Coastguard Worker        False if there was nothing to remove."""
870*cda5da8dSAndroid Build Coastguard Worker
871*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
872*cda5da8dSAndroid Build Coastguard Worker
873*cda5da8dSAndroid Build Coastguard Worker    def attach_loop(self, loop):
874*cda5da8dSAndroid Build Coastguard Worker        """Attach the watcher to an event loop.
875*cda5da8dSAndroid Build Coastguard Worker
876*cda5da8dSAndroid Build Coastguard Worker        If the watcher was previously attached to an event loop, then it is
877*cda5da8dSAndroid Build Coastguard Worker        first detached before attaching to the new loop.
878*cda5da8dSAndroid Build Coastguard Worker
879*cda5da8dSAndroid Build Coastguard Worker        Note: loop may be None.
880*cda5da8dSAndroid Build Coastguard Worker        """
881*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
882*cda5da8dSAndroid Build Coastguard Worker
883*cda5da8dSAndroid Build Coastguard Worker    def close(self):
884*cda5da8dSAndroid Build Coastguard Worker        """Close the watcher.
885*cda5da8dSAndroid Build Coastguard Worker
886*cda5da8dSAndroid Build Coastguard Worker        This must be called to make sure that any underlying resource is freed.
887*cda5da8dSAndroid Build Coastguard Worker        """
888*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
889*cda5da8dSAndroid Build Coastguard Worker
890*cda5da8dSAndroid Build Coastguard Worker    def is_active(self):
891*cda5da8dSAndroid Build Coastguard Worker        """Return ``True`` if the watcher is active and is used by the event loop.
892*cda5da8dSAndroid Build Coastguard Worker
893*cda5da8dSAndroid Build Coastguard Worker        Return True if the watcher is installed and ready to handle process exit
894*cda5da8dSAndroid Build Coastguard Worker        notifications.
895*cda5da8dSAndroid Build Coastguard Worker
896*cda5da8dSAndroid Build Coastguard Worker        """
897*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
898*cda5da8dSAndroid Build Coastguard Worker
899*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
900*cda5da8dSAndroid Build Coastguard Worker        """Enter the watcher's context and allow starting new processes
901*cda5da8dSAndroid Build Coastguard Worker
902*cda5da8dSAndroid Build Coastguard Worker        This function must return self"""
903*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
904*cda5da8dSAndroid Build Coastguard Worker
905*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, a, b, c):
906*cda5da8dSAndroid Build Coastguard Worker        """Exit the watcher's context"""
907*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
908*cda5da8dSAndroid Build Coastguard Worker
909*cda5da8dSAndroid Build Coastguard Worker
910*cda5da8dSAndroid Build Coastguard Workerclass PidfdChildWatcher(AbstractChildWatcher):
911*cda5da8dSAndroid Build Coastguard Worker    """Child watcher implementation using Linux's pid file descriptors.
912*cda5da8dSAndroid Build Coastguard Worker
913*cda5da8dSAndroid Build Coastguard Worker    This child watcher polls process file descriptors (pidfds) to await child
914*cda5da8dSAndroid Build Coastguard Worker    process termination. In some respects, PidfdChildWatcher is a "Goldilocks"
915*cda5da8dSAndroid Build Coastguard Worker    child watcher implementation. It doesn't require signals or threads, doesn't
916*cda5da8dSAndroid Build Coastguard Worker    interfere with any processes launched outside the event loop, and scales
917*cda5da8dSAndroid Build Coastguard Worker    linearly with the number of subprocesses launched by the event loop. The
918*cda5da8dSAndroid Build Coastguard Worker    main disadvantage is that pidfds are specific to Linux, and only work on
919*cda5da8dSAndroid Build Coastguard Worker    recent (5.3+) kernels.
920*cda5da8dSAndroid Build Coastguard Worker    """
921*cda5da8dSAndroid Build Coastguard Worker
922*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
923*cda5da8dSAndroid Build Coastguard Worker        self._loop = None
924*cda5da8dSAndroid Build Coastguard Worker        self._callbacks = {}
925*cda5da8dSAndroid Build Coastguard Worker
926*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
927*cda5da8dSAndroid Build Coastguard Worker        return self
928*cda5da8dSAndroid Build Coastguard Worker
929*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, exc_type, exc_value, exc_traceback):
930*cda5da8dSAndroid Build Coastguard Worker        pass
931*cda5da8dSAndroid Build Coastguard Worker
932*cda5da8dSAndroid Build Coastguard Worker    def is_active(self):
933*cda5da8dSAndroid Build Coastguard Worker        return self._loop is not None and self._loop.is_running()
934*cda5da8dSAndroid Build Coastguard Worker
935*cda5da8dSAndroid Build Coastguard Worker    def close(self):
936*cda5da8dSAndroid Build Coastguard Worker        self.attach_loop(None)
937*cda5da8dSAndroid Build Coastguard Worker
938*cda5da8dSAndroid Build Coastguard Worker    def attach_loop(self, loop):
939*cda5da8dSAndroid Build Coastguard Worker        if self._loop is not None and loop is None and self._callbacks:
940*cda5da8dSAndroid Build Coastguard Worker            warnings.warn(
941*cda5da8dSAndroid Build Coastguard Worker                'A loop is being detached '
942*cda5da8dSAndroid Build Coastguard Worker                'from a child watcher with pending handlers',
943*cda5da8dSAndroid Build Coastguard Worker                RuntimeWarning)
944*cda5da8dSAndroid Build Coastguard Worker        for pidfd, _, _ in self._callbacks.values():
945*cda5da8dSAndroid Build Coastguard Worker            self._loop._remove_reader(pidfd)
946*cda5da8dSAndroid Build Coastguard Worker            os.close(pidfd)
947*cda5da8dSAndroid Build Coastguard Worker        self._callbacks.clear()
948*cda5da8dSAndroid Build Coastguard Worker        self._loop = loop
949*cda5da8dSAndroid Build Coastguard Worker
950*cda5da8dSAndroid Build Coastguard Worker    def add_child_handler(self, pid, callback, *args):
951*cda5da8dSAndroid Build Coastguard Worker        existing = self._callbacks.get(pid)
952*cda5da8dSAndroid Build Coastguard Worker        if existing is not None:
953*cda5da8dSAndroid Build Coastguard Worker            self._callbacks[pid] = existing[0], callback, args
954*cda5da8dSAndroid Build Coastguard Worker        else:
955*cda5da8dSAndroid Build Coastguard Worker            pidfd = os.pidfd_open(pid)
956*cda5da8dSAndroid Build Coastguard Worker            self._loop._add_reader(pidfd, self._do_wait, pid)
957*cda5da8dSAndroid Build Coastguard Worker            self._callbacks[pid] = pidfd, callback, args
958*cda5da8dSAndroid Build Coastguard Worker
959*cda5da8dSAndroid Build Coastguard Worker    def _do_wait(self, pid):
960*cda5da8dSAndroid Build Coastguard Worker        pidfd, callback, args = self._callbacks.pop(pid)
961*cda5da8dSAndroid Build Coastguard Worker        self._loop._remove_reader(pidfd)
962*cda5da8dSAndroid Build Coastguard Worker        try:
963*cda5da8dSAndroid Build Coastguard Worker            _, status = os.waitpid(pid, 0)
964*cda5da8dSAndroid Build Coastguard Worker        except ChildProcessError:
965*cda5da8dSAndroid Build Coastguard Worker            # The child process is already reaped
966*cda5da8dSAndroid Build Coastguard Worker            # (may happen if waitpid() is called elsewhere).
967*cda5da8dSAndroid Build Coastguard Worker            returncode = 255
968*cda5da8dSAndroid Build Coastguard Worker            logger.warning(
969*cda5da8dSAndroid Build Coastguard Worker                "child process pid %d exit status already read: "
970*cda5da8dSAndroid Build Coastguard Worker                " will report returncode 255",
971*cda5da8dSAndroid Build Coastguard Worker                pid)
972*cda5da8dSAndroid Build Coastguard Worker        else:
973*cda5da8dSAndroid Build Coastguard Worker            returncode = waitstatus_to_exitcode(status)
974*cda5da8dSAndroid Build Coastguard Worker
975*cda5da8dSAndroid Build Coastguard Worker        os.close(pidfd)
976*cda5da8dSAndroid Build Coastguard Worker        callback(pid, returncode, *args)
977*cda5da8dSAndroid Build Coastguard Worker
978*cda5da8dSAndroid Build Coastguard Worker    def remove_child_handler(self, pid):
979*cda5da8dSAndroid Build Coastguard Worker        try:
980*cda5da8dSAndroid Build Coastguard Worker            pidfd, _, _ = self._callbacks.pop(pid)
981*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
982*cda5da8dSAndroid Build Coastguard Worker            return False
983*cda5da8dSAndroid Build Coastguard Worker        self._loop._remove_reader(pidfd)
984*cda5da8dSAndroid Build Coastguard Worker        os.close(pidfd)
985*cda5da8dSAndroid Build Coastguard Worker        return True
986*cda5da8dSAndroid Build Coastguard Worker
987*cda5da8dSAndroid Build Coastguard Worker
988*cda5da8dSAndroid Build Coastguard Workerclass BaseChildWatcher(AbstractChildWatcher):
989*cda5da8dSAndroid Build Coastguard Worker
990*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
991*cda5da8dSAndroid Build Coastguard Worker        self._loop = None
992*cda5da8dSAndroid Build Coastguard Worker        self._callbacks = {}
993*cda5da8dSAndroid Build Coastguard Worker
994*cda5da8dSAndroid Build Coastguard Worker    def close(self):
995*cda5da8dSAndroid Build Coastguard Worker        self.attach_loop(None)
996*cda5da8dSAndroid Build Coastguard Worker
997*cda5da8dSAndroid Build Coastguard Worker    def is_active(self):
998*cda5da8dSAndroid Build Coastguard Worker        return self._loop is not None and self._loop.is_running()
999*cda5da8dSAndroid Build Coastguard Worker
1000*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid(self, expected_pid):
1001*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
1002*cda5da8dSAndroid Build Coastguard Worker
1003*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid_all(self):
1004*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError()
1005*cda5da8dSAndroid Build Coastguard Worker
1006*cda5da8dSAndroid Build Coastguard Worker    def attach_loop(self, loop):
1007*cda5da8dSAndroid Build Coastguard Worker        assert loop is None or isinstance(loop, events.AbstractEventLoop)
1008*cda5da8dSAndroid Build Coastguard Worker
1009*cda5da8dSAndroid Build Coastguard Worker        if self._loop is not None and loop is None and self._callbacks:
1010*cda5da8dSAndroid Build Coastguard Worker            warnings.warn(
1011*cda5da8dSAndroid Build Coastguard Worker                'A loop is being detached '
1012*cda5da8dSAndroid Build Coastguard Worker                'from a child watcher with pending handlers',
1013*cda5da8dSAndroid Build Coastguard Worker                RuntimeWarning)
1014*cda5da8dSAndroid Build Coastguard Worker
1015*cda5da8dSAndroid Build Coastguard Worker        if self._loop is not None:
1016*cda5da8dSAndroid Build Coastguard Worker            self._loop.remove_signal_handler(signal.SIGCHLD)
1017*cda5da8dSAndroid Build Coastguard Worker
1018*cda5da8dSAndroid Build Coastguard Worker        self._loop = loop
1019*cda5da8dSAndroid Build Coastguard Worker        if loop is not None:
1020*cda5da8dSAndroid Build Coastguard Worker            loop.add_signal_handler(signal.SIGCHLD, self._sig_chld)
1021*cda5da8dSAndroid Build Coastguard Worker
1022*cda5da8dSAndroid Build Coastguard Worker            # Prevent a race condition in case a child terminated
1023*cda5da8dSAndroid Build Coastguard Worker            # during the switch.
1024*cda5da8dSAndroid Build Coastguard Worker            self._do_waitpid_all()
1025*cda5da8dSAndroid Build Coastguard Worker
1026*cda5da8dSAndroid Build Coastguard Worker    def _sig_chld(self):
1027*cda5da8dSAndroid Build Coastguard Worker        try:
1028*cda5da8dSAndroid Build Coastguard Worker            self._do_waitpid_all()
1029*cda5da8dSAndroid Build Coastguard Worker        except (SystemExit, KeyboardInterrupt):
1030*cda5da8dSAndroid Build Coastguard Worker            raise
1031*cda5da8dSAndroid Build Coastguard Worker        except BaseException as exc:
1032*cda5da8dSAndroid Build Coastguard Worker            # self._loop should always be available here
1033*cda5da8dSAndroid Build Coastguard Worker            # as '_sig_chld' is added as a signal handler
1034*cda5da8dSAndroid Build Coastguard Worker            # in 'attach_loop'
1035*cda5da8dSAndroid Build Coastguard Worker            self._loop.call_exception_handler({
1036*cda5da8dSAndroid Build Coastguard Worker                'message': 'Unknown exception in SIGCHLD handler',
1037*cda5da8dSAndroid Build Coastguard Worker                'exception': exc,
1038*cda5da8dSAndroid Build Coastguard Worker            })
1039*cda5da8dSAndroid Build Coastguard Worker
1040*cda5da8dSAndroid Build Coastguard Worker
1041*cda5da8dSAndroid Build Coastguard Workerclass SafeChildWatcher(BaseChildWatcher):
1042*cda5da8dSAndroid Build Coastguard Worker    """'Safe' child watcher implementation.
1043*cda5da8dSAndroid Build Coastguard Worker
1044*cda5da8dSAndroid Build Coastguard Worker    This implementation avoids disrupting other code spawning processes by
1045*cda5da8dSAndroid Build Coastguard Worker    polling explicitly each process in the SIGCHLD handler instead of calling
1046*cda5da8dSAndroid Build Coastguard Worker    os.waitpid(-1).
1047*cda5da8dSAndroid Build Coastguard Worker
1048*cda5da8dSAndroid Build Coastguard Worker    This is a safe solution but it has a significant overhead when handling a
1049*cda5da8dSAndroid Build Coastguard Worker    big number of children (O(n) each time SIGCHLD is raised)
1050*cda5da8dSAndroid Build Coastguard Worker    """
1051*cda5da8dSAndroid Build Coastguard Worker
1052*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1053*cda5da8dSAndroid Build Coastguard Worker        self._callbacks.clear()
1054*cda5da8dSAndroid Build Coastguard Worker        super().close()
1055*cda5da8dSAndroid Build Coastguard Worker
1056*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
1057*cda5da8dSAndroid Build Coastguard Worker        return self
1058*cda5da8dSAndroid Build Coastguard Worker
1059*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, a, b, c):
1060*cda5da8dSAndroid Build Coastguard Worker        pass
1061*cda5da8dSAndroid Build Coastguard Worker
1062*cda5da8dSAndroid Build Coastguard Worker    def add_child_handler(self, pid, callback, *args):
1063*cda5da8dSAndroid Build Coastguard Worker        self._callbacks[pid] = (callback, args)
1064*cda5da8dSAndroid Build Coastguard Worker
1065*cda5da8dSAndroid Build Coastguard Worker        # Prevent a race condition in case the child is already terminated.
1066*cda5da8dSAndroid Build Coastguard Worker        self._do_waitpid(pid)
1067*cda5da8dSAndroid Build Coastguard Worker
1068*cda5da8dSAndroid Build Coastguard Worker    def remove_child_handler(self, pid):
1069*cda5da8dSAndroid Build Coastguard Worker        try:
1070*cda5da8dSAndroid Build Coastguard Worker            del self._callbacks[pid]
1071*cda5da8dSAndroid Build Coastguard Worker            return True
1072*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
1073*cda5da8dSAndroid Build Coastguard Worker            return False
1074*cda5da8dSAndroid Build Coastguard Worker
1075*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid_all(self):
1076*cda5da8dSAndroid Build Coastguard Worker
1077*cda5da8dSAndroid Build Coastguard Worker        for pid in list(self._callbacks):
1078*cda5da8dSAndroid Build Coastguard Worker            self._do_waitpid(pid)
1079*cda5da8dSAndroid Build Coastguard Worker
1080*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid(self, expected_pid):
1081*cda5da8dSAndroid Build Coastguard Worker        assert expected_pid > 0
1082*cda5da8dSAndroid Build Coastguard Worker
1083*cda5da8dSAndroid Build Coastguard Worker        try:
1084*cda5da8dSAndroid Build Coastguard Worker            pid, status = os.waitpid(expected_pid, os.WNOHANG)
1085*cda5da8dSAndroid Build Coastguard Worker        except ChildProcessError:
1086*cda5da8dSAndroid Build Coastguard Worker            # The child process is already reaped
1087*cda5da8dSAndroid Build Coastguard Worker            # (may happen if waitpid() is called elsewhere).
1088*cda5da8dSAndroid Build Coastguard Worker            pid = expected_pid
1089*cda5da8dSAndroid Build Coastguard Worker            returncode = 255
1090*cda5da8dSAndroid Build Coastguard Worker            logger.warning(
1091*cda5da8dSAndroid Build Coastguard Worker                "Unknown child process pid %d, will report returncode 255",
1092*cda5da8dSAndroid Build Coastguard Worker                pid)
1093*cda5da8dSAndroid Build Coastguard Worker        else:
1094*cda5da8dSAndroid Build Coastguard Worker            if pid == 0:
1095*cda5da8dSAndroid Build Coastguard Worker                # The child process is still alive.
1096*cda5da8dSAndroid Build Coastguard Worker                return
1097*cda5da8dSAndroid Build Coastguard Worker
1098*cda5da8dSAndroid Build Coastguard Worker            returncode = waitstatus_to_exitcode(status)
1099*cda5da8dSAndroid Build Coastguard Worker            if self._loop.get_debug():
1100*cda5da8dSAndroid Build Coastguard Worker                logger.debug('process %s exited with returncode %s',
1101*cda5da8dSAndroid Build Coastguard Worker                             expected_pid, returncode)
1102*cda5da8dSAndroid Build Coastguard Worker
1103*cda5da8dSAndroid Build Coastguard Worker        try:
1104*cda5da8dSAndroid Build Coastguard Worker            callback, args = self._callbacks.pop(pid)
1105*cda5da8dSAndroid Build Coastguard Worker        except KeyError:  # pragma: no cover
1106*cda5da8dSAndroid Build Coastguard Worker            # May happen if .remove_child_handler() is called
1107*cda5da8dSAndroid Build Coastguard Worker            # after os.waitpid() returns.
1108*cda5da8dSAndroid Build Coastguard Worker            if self._loop.get_debug():
1109*cda5da8dSAndroid Build Coastguard Worker                logger.warning("Child watcher got an unexpected pid: %r",
1110*cda5da8dSAndroid Build Coastguard Worker                               pid, exc_info=True)
1111*cda5da8dSAndroid Build Coastguard Worker        else:
1112*cda5da8dSAndroid Build Coastguard Worker            callback(pid, returncode, *args)
1113*cda5da8dSAndroid Build Coastguard Worker
1114*cda5da8dSAndroid Build Coastguard Worker
1115*cda5da8dSAndroid Build Coastguard Workerclass FastChildWatcher(BaseChildWatcher):
1116*cda5da8dSAndroid Build Coastguard Worker    """'Fast' child watcher implementation.
1117*cda5da8dSAndroid Build Coastguard Worker
1118*cda5da8dSAndroid Build Coastguard Worker    This implementation reaps every terminated processes by calling
1119*cda5da8dSAndroid Build Coastguard Worker    os.waitpid(-1) directly, possibly breaking other code spawning processes
1120*cda5da8dSAndroid Build Coastguard Worker    and waiting for their termination.
1121*cda5da8dSAndroid Build Coastguard Worker
1122*cda5da8dSAndroid Build Coastguard Worker    There is no noticeable overhead when handling a big number of children
1123*cda5da8dSAndroid Build Coastguard Worker    (O(1) each time a child terminates).
1124*cda5da8dSAndroid Build Coastguard Worker    """
1125*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
1126*cda5da8dSAndroid Build Coastguard Worker        super().__init__()
1127*cda5da8dSAndroid Build Coastguard Worker        self._lock = threading.Lock()
1128*cda5da8dSAndroid Build Coastguard Worker        self._zombies = {}
1129*cda5da8dSAndroid Build Coastguard Worker        self._forks = 0
1130*cda5da8dSAndroid Build Coastguard Worker
1131*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1132*cda5da8dSAndroid Build Coastguard Worker        self._callbacks.clear()
1133*cda5da8dSAndroid Build Coastguard Worker        self._zombies.clear()
1134*cda5da8dSAndroid Build Coastguard Worker        super().close()
1135*cda5da8dSAndroid Build Coastguard Worker
1136*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
1137*cda5da8dSAndroid Build Coastguard Worker        with self._lock:
1138*cda5da8dSAndroid Build Coastguard Worker            self._forks += 1
1139*cda5da8dSAndroid Build Coastguard Worker
1140*cda5da8dSAndroid Build Coastguard Worker            return self
1141*cda5da8dSAndroid Build Coastguard Worker
1142*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, a, b, c):
1143*cda5da8dSAndroid Build Coastguard Worker        with self._lock:
1144*cda5da8dSAndroid Build Coastguard Worker            self._forks -= 1
1145*cda5da8dSAndroid Build Coastguard Worker
1146*cda5da8dSAndroid Build Coastguard Worker            if self._forks or not self._zombies:
1147*cda5da8dSAndroid Build Coastguard Worker                return
1148*cda5da8dSAndroid Build Coastguard Worker
1149*cda5da8dSAndroid Build Coastguard Worker            collateral_victims = str(self._zombies)
1150*cda5da8dSAndroid Build Coastguard Worker            self._zombies.clear()
1151*cda5da8dSAndroid Build Coastguard Worker
1152*cda5da8dSAndroid Build Coastguard Worker        logger.warning(
1153*cda5da8dSAndroid Build Coastguard Worker            "Caught subprocesses termination from unknown pids: %s",
1154*cda5da8dSAndroid Build Coastguard Worker            collateral_victims)
1155*cda5da8dSAndroid Build Coastguard Worker
1156*cda5da8dSAndroid Build Coastguard Worker    def add_child_handler(self, pid, callback, *args):
1157*cda5da8dSAndroid Build Coastguard Worker        assert self._forks, "Must use the context manager"
1158*cda5da8dSAndroid Build Coastguard Worker
1159*cda5da8dSAndroid Build Coastguard Worker        with self._lock:
1160*cda5da8dSAndroid Build Coastguard Worker            try:
1161*cda5da8dSAndroid Build Coastguard Worker                returncode = self._zombies.pop(pid)
1162*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
1163*cda5da8dSAndroid Build Coastguard Worker                # The child is running.
1164*cda5da8dSAndroid Build Coastguard Worker                self._callbacks[pid] = callback, args
1165*cda5da8dSAndroid Build Coastguard Worker                return
1166*cda5da8dSAndroid Build Coastguard Worker
1167*cda5da8dSAndroid Build Coastguard Worker        # The child is dead already. We can fire the callback.
1168*cda5da8dSAndroid Build Coastguard Worker        callback(pid, returncode, *args)
1169*cda5da8dSAndroid Build Coastguard Worker
1170*cda5da8dSAndroid Build Coastguard Worker    def remove_child_handler(self, pid):
1171*cda5da8dSAndroid Build Coastguard Worker        try:
1172*cda5da8dSAndroid Build Coastguard Worker            del self._callbacks[pid]
1173*cda5da8dSAndroid Build Coastguard Worker            return True
1174*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
1175*cda5da8dSAndroid Build Coastguard Worker            return False
1176*cda5da8dSAndroid Build Coastguard Worker
1177*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid_all(self):
1178*cda5da8dSAndroid Build Coastguard Worker        # Because of signal coalescing, we must keep calling waitpid() as
1179*cda5da8dSAndroid Build Coastguard Worker        # long as we're able to reap a child.
1180*cda5da8dSAndroid Build Coastguard Worker        while True:
1181*cda5da8dSAndroid Build Coastguard Worker            try:
1182*cda5da8dSAndroid Build Coastguard Worker                pid, status = os.waitpid(-1, os.WNOHANG)
1183*cda5da8dSAndroid Build Coastguard Worker            except ChildProcessError:
1184*cda5da8dSAndroid Build Coastguard Worker                # No more child processes exist.
1185*cda5da8dSAndroid Build Coastguard Worker                return
1186*cda5da8dSAndroid Build Coastguard Worker            else:
1187*cda5da8dSAndroid Build Coastguard Worker                if pid == 0:
1188*cda5da8dSAndroid Build Coastguard Worker                    # A child process is still alive.
1189*cda5da8dSAndroid Build Coastguard Worker                    return
1190*cda5da8dSAndroid Build Coastguard Worker
1191*cda5da8dSAndroid Build Coastguard Worker                returncode = waitstatus_to_exitcode(status)
1192*cda5da8dSAndroid Build Coastguard Worker
1193*cda5da8dSAndroid Build Coastguard Worker            with self._lock:
1194*cda5da8dSAndroid Build Coastguard Worker                try:
1195*cda5da8dSAndroid Build Coastguard Worker                    callback, args = self._callbacks.pop(pid)
1196*cda5da8dSAndroid Build Coastguard Worker                except KeyError:
1197*cda5da8dSAndroid Build Coastguard Worker                    # unknown child
1198*cda5da8dSAndroid Build Coastguard Worker                    if self._forks:
1199*cda5da8dSAndroid Build Coastguard Worker                        # It may not be registered yet.
1200*cda5da8dSAndroid Build Coastguard Worker                        self._zombies[pid] = returncode
1201*cda5da8dSAndroid Build Coastguard Worker                        if self._loop.get_debug():
1202*cda5da8dSAndroid Build Coastguard Worker                            logger.debug('unknown process %s exited '
1203*cda5da8dSAndroid Build Coastguard Worker                                         'with returncode %s',
1204*cda5da8dSAndroid Build Coastguard Worker                                         pid, returncode)
1205*cda5da8dSAndroid Build Coastguard Worker                        continue
1206*cda5da8dSAndroid Build Coastguard Worker                    callback = None
1207*cda5da8dSAndroid Build Coastguard Worker                else:
1208*cda5da8dSAndroid Build Coastguard Worker                    if self._loop.get_debug():
1209*cda5da8dSAndroid Build Coastguard Worker                        logger.debug('process %s exited with returncode %s',
1210*cda5da8dSAndroid Build Coastguard Worker                                     pid, returncode)
1211*cda5da8dSAndroid Build Coastguard Worker
1212*cda5da8dSAndroid Build Coastguard Worker            if callback is None:
1213*cda5da8dSAndroid Build Coastguard Worker                logger.warning(
1214*cda5da8dSAndroid Build Coastguard Worker                    "Caught subprocess termination from unknown pid: "
1215*cda5da8dSAndroid Build Coastguard Worker                    "%d -> %d", pid, returncode)
1216*cda5da8dSAndroid Build Coastguard Worker            else:
1217*cda5da8dSAndroid Build Coastguard Worker                callback(pid, returncode, *args)
1218*cda5da8dSAndroid Build Coastguard Worker
1219*cda5da8dSAndroid Build Coastguard Worker
1220*cda5da8dSAndroid Build Coastguard Workerclass MultiLoopChildWatcher(AbstractChildWatcher):
1221*cda5da8dSAndroid Build Coastguard Worker    """A watcher that doesn't require running loop in the main thread.
1222*cda5da8dSAndroid Build Coastguard Worker
1223*cda5da8dSAndroid Build Coastguard Worker    This implementation registers a SIGCHLD signal handler on
1224*cda5da8dSAndroid Build Coastguard Worker    instantiation (which may conflict with other code that
1225*cda5da8dSAndroid Build Coastguard Worker    install own handler for this signal).
1226*cda5da8dSAndroid Build Coastguard Worker
1227*cda5da8dSAndroid Build Coastguard Worker    The solution is safe but it has a significant overhead when
1228*cda5da8dSAndroid Build Coastguard Worker    handling a big number of processes (*O(n)* each time a
1229*cda5da8dSAndroid Build Coastguard Worker    SIGCHLD is received).
1230*cda5da8dSAndroid Build Coastguard Worker    """
1231*cda5da8dSAndroid Build Coastguard Worker
1232*cda5da8dSAndroid Build Coastguard Worker    # Implementation note:
1233*cda5da8dSAndroid Build Coastguard Worker    # The class keeps compatibility with AbstractChildWatcher ABC
1234*cda5da8dSAndroid Build Coastguard Worker    # To achieve this it has empty attach_loop() method
1235*cda5da8dSAndroid Build Coastguard Worker    # and doesn't accept explicit loop argument
1236*cda5da8dSAndroid Build Coastguard Worker    # for add_child_handler()/remove_child_handler()
1237*cda5da8dSAndroid Build Coastguard Worker    # but retrieves the current loop by get_running_loop()
1238*cda5da8dSAndroid Build Coastguard Worker
1239*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
1240*cda5da8dSAndroid Build Coastguard Worker        self._callbacks = {}
1241*cda5da8dSAndroid Build Coastguard Worker        self._saved_sighandler = None
1242*cda5da8dSAndroid Build Coastguard Worker
1243*cda5da8dSAndroid Build Coastguard Worker    def is_active(self):
1244*cda5da8dSAndroid Build Coastguard Worker        return self._saved_sighandler is not None
1245*cda5da8dSAndroid Build Coastguard Worker
1246*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1247*cda5da8dSAndroid Build Coastguard Worker        self._callbacks.clear()
1248*cda5da8dSAndroid Build Coastguard Worker        if self._saved_sighandler is None:
1249*cda5da8dSAndroid Build Coastguard Worker            return
1250*cda5da8dSAndroid Build Coastguard Worker
1251*cda5da8dSAndroid Build Coastguard Worker        handler = signal.getsignal(signal.SIGCHLD)
1252*cda5da8dSAndroid Build Coastguard Worker        if handler != self._sig_chld:
1253*cda5da8dSAndroid Build Coastguard Worker            logger.warning("SIGCHLD handler was changed by outside code")
1254*cda5da8dSAndroid Build Coastguard Worker        else:
1255*cda5da8dSAndroid Build Coastguard Worker            signal.signal(signal.SIGCHLD, self._saved_sighandler)
1256*cda5da8dSAndroid Build Coastguard Worker        self._saved_sighandler = None
1257*cda5da8dSAndroid Build Coastguard Worker
1258*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
1259*cda5da8dSAndroid Build Coastguard Worker        return self
1260*cda5da8dSAndroid Build Coastguard Worker
1261*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, exc_type, exc_val, exc_tb):
1262*cda5da8dSAndroid Build Coastguard Worker        pass
1263*cda5da8dSAndroid Build Coastguard Worker
1264*cda5da8dSAndroid Build Coastguard Worker    def add_child_handler(self, pid, callback, *args):
1265*cda5da8dSAndroid Build Coastguard Worker        loop = events.get_running_loop()
1266*cda5da8dSAndroid Build Coastguard Worker        self._callbacks[pid] = (loop, callback, args)
1267*cda5da8dSAndroid Build Coastguard Worker
1268*cda5da8dSAndroid Build Coastguard Worker        # Prevent a race condition in case the child is already terminated.
1269*cda5da8dSAndroid Build Coastguard Worker        self._do_waitpid(pid)
1270*cda5da8dSAndroid Build Coastguard Worker
1271*cda5da8dSAndroid Build Coastguard Worker    def remove_child_handler(self, pid):
1272*cda5da8dSAndroid Build Coastguard Worker        try:
1273*cda5da8dSAndroid Build Coastguard Worker            del self._callbacks[pid]
1274*cda5da8dSAndroid Build Coastguard Worker            return True
1275*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
1276*cda5da8dSAndroid Build Coastguard Worker            return False
1277*cda5da8dSAndroid Build Coastguard Worker
1278*cda5da8dSAndroid Build Coastguard Worker    def attach_loop(self, loop):
1279*cda5da8dSAndroid Build Coastguard Worker        # Don't save the loop but initialize itself if called first time
1280*cda5da8dSAndroid Build Coastguard Worker        # The reason to do it here is that attach_loop() is called from
1281*cda5da8dSAndroid Build Coastguard Worker        # unix policy only for the main thread.
1282*cda5da8dSAndroid Build Coastguard Worker        # Main thread is required for subscription on SIGCHLD signal
1283*cda5da8dSAndroid Build Coastguard Worker        if self._saved_sighandler is not None:
1284*cda5da8dSAndroid Build Coastguard Worker            return
1285*cda5da8dSAndroid Build Coastguard Worker
1286*cda5da8dSAndroid Build Coastguard Worker        self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld)
1287*cda5da8dSAndroid Build Coastguard Worker        if self._saved_sighandler is None:
1288*cda5da8dSAndroid Build Coastguard Worker            logger.warning("Previous SIGCHLD handler was set by non-Python code, "
1289*cda5da8dSAndroid Build Coastguard Worker                           "restore to default handler on watcher close.")
1290*cda5da8dSAndroid Build Coastguard Worker            self._saved_sighandler = signal.SIG_DFL
1291*cda5da8dSAndroid Build Coastguard Worker
1292*cda5da8dSAndroid Build Coastguard Worker        # Set SA_RESTART to limit EINTR occurrences.
1293*cda5da8dSAndroid Build Coastguard Worker        signal.siginterrupt(signal.SIGCHLD, False)
1294*cda5da8dSAndroid Build Coastguard Worker
1295*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid_all(self):
1296*cda5da8dSAndroid Build Coastguard Worker        for pid in list(self._callbacks):
1297*cda5da8dSAndroid Build Coastguard Worker            self._do_waitpid(pid)
1298*cda5da8dSAndroid Build Coastguard Worker
1299*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid(self, expected_pid):
1300*cda5da8dSAndroid Build Coastguard Worker        assert expected_pid > 0
1301*cda5da8dSAndroid Build Coastguard Worker
1302*cda5da8dSAndroid Build Coastguard Worker        try:
1303*cda5da8dSAndroid Build Coastguard Worker            pid, status = os.waitpid(expected_pid, os.WNOHANG)
1304*cda5da8dSAndroid Build Coastguard Worker        except ChildProcessError:
1305*cda5da8dSAndroid Build Coastguard Worker            # The child process is already reaped
1306*cda5da8dSAndroid Build Coastguard Worker            # (may happen if waitpid() is called elsewhere).
1307*cda5da8dSAndroid Build Coastguard Worker            pid = expected_pid
1308*cda5da8dSAndroid Build Coastguard Worker            returncode = 255
1309*cda5da8dSAndroid Build Coastguard Worker            logger.warning(
1310*cda5da8dSAndroid Build Coastguard Worker                "Unknown child process pid %d, will report returncode 255",
1311*cda5da8dSAndroid Build Coastguard Worker                pid)
1312*cda5da8dSAndroid Build Coastguard Worker            debug_log = False
1313*cda5da8dSAndroid Build Coastguard Worker        else:
1314*cda5da8dSAndroid Build Coastguard Worker            if pid == 0:
1315*cda5da8dSAndroid Build Coastguard Worker                # The child process is still alive.
1316*cda5da8dSAndroid Build Coastguard Worker                return
1317*cda5da8dSAndroid Build Coastguard Worker
1318*cda5da8dSAndroid Build Coastguard Worker            returncode = waitstatus_to_exitcode(status)
1319*cda5da8dSAndroid Build Coastguard Worker            debug_log = True
1320*cda5da8dSAndroid Build Coastguard Worker        try:
1321*cda5da8dSAndroid Build Coastguard Worker            loop, callback, args = self._callbacks.pop(pid)
1322*cda5da8dSAndroid Build Coastguard Worker        except KeyError:  # pragma: no cover
1323*cda5da8dSAndroid Build Coastguard Worker            # May happen if .remove_child_handler() is called
1324*cda5da8dSAndroid Build Coastguard Worker            # after os.waitpid() returns.
1325*cda5da8dSAndroid Build Coastguard Worker            logger.warning("Child watcher got an unexpected pid: %r",
1326*cda5da8dSAndroid Build Coastguard Worker                           pid, exc_info=True)
1327*cda5da8dSAndroid Build Coastguard Worker        else:
1328*cda5da8dSAndroid Build Coastguard Worker            if loop.is_closed():
1329*cda5da8dSAndroid Build Coastguard Worker                logger.warning("Loop %r that handles pid %r is closed", loop, pid)
1330*cda5da8dSAndroid Build Coastguard Worker            else:
1331*cda5da8dSAndroid Build Coastguard Worker                if debug_log and loop.get_debug():
1332*cda5da8dSAndroid Build Coastguard Worker                    logger.debug('process %s exited with returncode %s',
1333*cda5da8dSAndroid Build Coastguard Worker                                 expected_pid, returncode)
1334*cda5da8dSAndroid Build Coastguard Worker                loop.call_soon_threadsafe(callback, pid, returncode, *args)
1335*cda5da8dSAndroid Build Coastguard Worker
1336*cda5da8dSAndroid Build Coastguard Worker    def _sig_chld(self, signum, frame):
1337*cda5da8dSAndroid Build Coastguard Worker        try:
1338*cda5da8dSAndroid Build Coastguard Worker            self._do_waitpid_all()
1339*cda5da8dSAndroid Build Coastguard Worker        except (SystemExit, KeyboardInterrupt):
1340*cda5da8dSAndroid Build Coastguard Worker            raise
1341*cda5da8dSAndroid Build Coastguard Worker        except BaseException:
1342*cda5da8dSAndroid Build Coastguard Worker            logger.warning('Unknown exception in SIGCHLD handler', exc_info=True)
1343*cda5da8dSAndroid Build Coastguard Worker
1344*cda5da8dSAndroid Build Coastguard Worker
1345*cda5da8dSAndroid Build Coastguard Workerclass ThreadedChildWatcher(AbstractChildWatcher):
1346*cda5da8dSAndroid Build Coastguard Worker    """Threaded child watcher implementation.
1347*cda5da8dSAndroid Build Coastguard Worker
1348*cda5da8dSAndroid Build Coastguard Worker    The watcher uses a thread per process
1349*cda5da8dSAndroid Build Coastguard Worker    for waiting for the process finish.
1350*cda5da8dSAndroid Build Coastguard Worker
1351*cda5da8dSAndroid Build Coastguard Worker    It doesn't require subscription on POSIX signal
1352*cda5da8dSAndroid Build Coastguard Worker    but a thread creation is not free.
1353*cda5da8dSAndroid Build Coastguard Worker
1354*cda5da8dSAndroid Build Coastguard Worker    The watcher has O(1) complexity, its performance doesn't depend
1355*cda5da8dSAndroid Build Coastguard Worker    on amount of spawn processes.
1356*cda5da8dSAndroid Build Coastguard Worker    """
1357*cda5da8dSAndroid Build Coastguard Worker
1358*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
1359*cda5da8dSAndroid Build Coastguard Worker        self._pid_counter = itertools.count(0)
1360*cda5da8dSAndroid Build Coastguard Worker        self._threads = {}
1361*cda5da8dSAndroid Build Coastguard Worker
1362*cda5da8dSAndroid Build Coastguard Worker    def is_active(self):
1363*cda5da8dSAndroid Build Coastguard Worker        return True
1364*cda5da8dSAndroid Build Coastguard Worker
1365*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1366*cda5da8dSAndroid Build Coastguard Worker        self._join_threads()
1367*cda5da8dSAndroid Build Coastguard Worker
1368*cda5da8dSAndroid Build Coastguard Worker    def _join_threads(self):
1369*cda5da8dSAndroid Build Coastguard Worker        """Internal: Join all non-daemon threads"""
1370*cda5da8dSAndroid Build Coastguard Worker        threads = [thread for thread in list(self._threads.values())
1371*cda5da8dSAndroid Build Coastguard Worker                   if thread.is_alive() and not thread.daemon]
1372*cda5da8dSAndroid Build Coastguard Worker        for thread in threads:
1373*cda5da8dSAndroid Build Coastguard Worker            thread.join()
1374*cda5da8dSAndroid Build Coastguard Worker
1375*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
1376*cda5da8dSAndroid Build Coastguard Worker        return self
1377*cda5da8dSAndroid Build Coastguard Worker
1378*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, exc_type, exc_val, exc_tb):
1379*cda5da8dSAndroid Build Coastguard Worker        pass
1380*cda5da8dSAndroid Build Coastguard Worker
1381*cda5da8dSAndroid Build Coastguard Worker    def __del__(self, _warn=warnings.warn):
1382*cda5da8dSAndroid Build Coastguard Worker        threads = [thread for thread in list(self._threads.values())
1383*cda5da8dSAndroid Build Coastguard Worker                   if thread.is_alive()]
1384*cda5da8dSAndroid Build Coastguard Worker        if threads:
1385*cda5da8dSAndroid Build Coastguard Worker            _warn(f"{self.__class__} has registered but not finished child processes",
1386*cda5da8dSAndroid Build Coastguard Worker                  ResourceWarning,
1387*cda5da8dSAndroid Build Coastguard Worker                  source=self)
1388*cda5da8dSAndroid Build Coastguard Worker
1389*cda5da8dSAndroid Build Coastguard Worker    def add_child_handler(self, pid, callback, *args):
1390*cda5da8dSAndroid Build Coastguard Worker        loop = events.get_running_loop()
1391*cda5da8dSAndroid Build Coastguard Worker        thread = threading.Thread(target=self._do_waitpid,
1392*cda5da8dSAndroid Build Coastguard Worker                                  name=f"waitpid-{next(self._pid_counter)}",
1393*cda5da8dSAndroid Build Coastguard Worker                                  args=(loop, pid, callback, args),
1394*cda5da8dSAndroid Build Coastguard Worker                                  daemon=True)
1395*cda5da8dSAndroid Build Coastguard Worker        self._threads[pid] = thread
1396*cda5da8dSAndroid Build Coastguard Worker        thread.start()
1397*cda5da8dSAndroid Build Coastguard Worker
1398*cda5da8dSAndroid Build Coastguard Worker    def remove_child_handler(self, pid):
1399*cda5da8dSAndroid Build Coastguard Worker        # asyncio never calls remove_child_handler() !!!
1400*cda5da8dSAndroid Build Coastguard Worker        # The method is no-op but is implemented because
1401*cda5da8dSAndroid Build Coastguard Worker        # abstract base classes require it.
1402*cda5da8dSAndroid Build Coastguard Worker        return True
1403*cda5da8dSAndroid Build Coastguard Worker
1404*cda5da8dSAndroid Build Coastguard Worker    def attach_loop(self, loop):
1405*cda5da8dSAndroid Build Coastguard Worker        pass
1406*cda5da8dSAndroid Build Coastguard Worker
1407*cda5da8dSAndroid Build Coastguard Worker    def _do_waitpid(self, loop, expected_pid, callback, args):
1408*cda5da8dSAndroid Build Coastguard Worker        assert expected_pid > 0
1409*cda5da8dSAndroid Build Coastguard Worker
1410*cda5da8dSAndroid Build Coastguard Worker        try:
1411*cda5da8dSAndroid Build Coastguard Worker            pid, status = os.waitpid(expected_pid, 0)
1412*cda5da8dSAndroid Build Coastguard Worker        except ChildProcessError:
1413*cda5da8dSAndroid Build Coastguard Worker            # The child process is already reaped
1414*cda5da8dSAndroid Build Coastguard Worker            # (may happen if waitpid() is called elsewhere).
1415*cda5da8dSAndroid Build Coastguard Worker            pid = expected_pid
1416*cda5da8dSAndroid Build Coastguard Worker            returncode = 255
1417*cda5da8dSAndroid Build Coastguard Worker            logger.warning(
1418*cda5da8dSAndroid Build Coastguard Worker                "Unknown child process pid %d, will report returncode 255",
1419*cda5da8dSAndroid Build Coastguard Worker                pid)
1420*cda5da8dSAndroid Build Coastguard Worker        else:
1421*cda5da8dSAndroid Build Coastguard Worker            returncode = waitstatus_to_exitcode(status)
1422*cda5da8dSAndroid Build Coastguard Worker            if loop.get_debug():
1423*cda5da8dSAndroid Build Coastguard Worker                logger.debug('process %s exited with returncode %s',
1424*cda5da8dSAndroid Build Coastguard Worker                             expected_pid, returncode)
1425*cda5da8dSAndroid Build Coastguard Worker
1426*cda5da8dSAndroid Build Coastguard Worker        if loop.is_closed():
1427*cda5da8dSAndroid Build Coastguard Worker            logger.warning("Loop %r that handles pid %r is closed", loop, pid)
1428*cda5da8dSAndroid Build Coastguard Worker        else:
1429*cda5da8dSAndroid Build Coastguard Worker            loop.call_soon_threadsafe(callback, pid, returncode, *args)
1430*cda5da8dSAndroid Build Coastguard Worker
1431*cda5da8dSAndroid Build Coastguard Worker        self._threads.pop(expected_pid)
1432*cda5da8dSAndroid Build Coastguard Worker
1433*cda5da8dSAndroid Build Coastguard Worker
1434*cda5da8dSAndroid Build Coastguard Workerclass _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
1435*cda5da8dSAndroid Build Coastguard Worker    """UNIX event loop policy with a watcher for child processes."""
1436*cda5da8dSAndroid Build Coastguard Worker    _loop_factory = _UnixSelectorEventLoop
1437*cda5da8dSAndroid Build Coastguard Worker
1438*cda5da8dSAndroid Build Coastguard Worker    def __init__(self):
1439*cda5da8dSAndroid Build Coastguard Worker        super().__init__()
1440*cda5da8dSAndroid Build Coastguard Worker        self._watcher = None
1441*cda5da8dSAndroid Build Coastguard Worker
1442*cda5da8dSAndroid Build Coastguard Worker    def _init_watcher(self):
1443*cda5da8dSAndroid Build Coastguard Worker        with events._lock:
1444*cda5da8dSAndroid Build Coastguard Worker            if self._watcher is None:  # pragma: no branch
1445*cda5da8dSAndroid Build Coastguard Worker                self._watcher = ThreadedChildWatcher()
1446*cda5da8dSAndroid Build Coastguard Worker                if threading.current_thread() is threading.main_thread():
1447*cda5da8dSAndroid Build Coastguard Worker                    self._watcher.attach_loop(self._local._loop)
1448*cda5da8dSAndroid Build Coastguard Worker
1449*cda5da8dSAndroid Build Coastguard Worker    def set_event_loop(self, loop):
1450*cda5da8dSAndroid Build Coastguard Worker        """Set the event loop.
1451*cda5da8dSAndroid Build Coastguard Worker
1452*cda5da8dSAndroid Build Coastguard Worker        As a side effect, if a child watcher was set before, then calling
1453*cda5da8dSAndroid Build Coastguard Worker        .set_event_loop() from the main thread will call .attach_loop(loop) on
1454*cda5da8dSAndroid Build Coastguard Worker        the child watcher.
1455*cda5da8dSAndroid Build Coastguard Worker        """
1456*cda5da8dSAndroid Build Coastguard Worker
1457*cda5da8dSAndroid Build Coastguard Worker        super().set_event_loop(loop)
1458*cda5da8dSAndroid Build Coastguard Worker
1459*cda5da8dSAndroid Build Coastguard Worker        if (self._watcher is not None and
1460*cda5da8dSAndroid Build Coastguard Worker                threading.current_thread() is threading.main_thread()):
1461*cda5da8dSAndroid Build Coastguard Worker            self._watcher.attach_loop(loop)
1462*cda5da8dSAndroid Build Coastguard Worker
1463*cda5da8dSAndroid Build Coastguard Worker    def get_child_watcher(self):
1464*cda5da8dSAndroid Build Coastguard Worker        """Get the watcher for child processes.
1465*cda5da8dSAndroid Build Coastguard Worker
1466*cda5da8dSAndroid Build Coastguard Worker        If not yet set, a ThreadedChildWatcher object is automatically created.
1467*cda5da8dSAndroid Build Coastguard Worker        """
1468*cda5da8dSAndroid Build Coastguard Worker        if self._watcher is None:
1469*cda5da8dSAndroid Build Coastguard Worker            self._init_watcher()
1470*cda5da8dSAndroid Build Coastguard Worker
1471*cda5da8dSAndroid Build Coastguard Worker        return self._watcher
1472*cda5da8dSAndroid Build Coastguard Worker
1473*cda5da8dSAndroid Build Coastguard Worker    def set_child_watcher(self, watcher):
1474*cda5da8dSAndroid Build Coastguard Worker        """Set the watcher for child processes."""
1475*cda5da8dSAndroid Build Coastguard Worker
1476*cda5da8dSAndroid Build Coastguard Worker        assert watcher is None or isinstance(watcher, AbstractChildWatcher)
1477*cda5da8dSAndroid Build Coastguard Worker
1478*cda5da8dSAndroid Build Coastguard Worker        if self._watcher is not None:
1479*cda5da8dSAndroid Build Coastguard Worker            self._watcher.close()
1480*cda5da8dSAndroid Build Coastguard Worker
1481*cda5da8dSAndroid Build Coastguard Worker        self._watcher = watcher
1482*cda5da8dSAndroid Build Coastguard Worker
1483*cda5da8dSAndroid Build Coastguard Worker
1484*cda5da8dSAndroid Build Coastguard WorkerSelectorEventLoop = _UnixSelectorEventLoop
1485*cda5da8dSAndroid Build Coastguard WorkerDefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
1486