| Yury Selivanov | dec1a45 | 2014-02-18 22:27:48 -0500 | [diff] [blame] | 1 | """Selector event loop for Unix with signal handling.""" | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 2 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 3 | import errno | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 4 | import io | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 5 | import os | 
| Victor Stinner | 4271dfd | 2017-11-28 15:19:56 +0100 | [diff] [blame] | 6 | import selectors | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 7 | import signal | 
 | 8 | import socket | 
 | 9 | import stat | 
 | 10 | import subprocess | 
 | 11 | import sys | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 12 | import threading | 
| Victor Stinner | 978a9af | 2015-01-29 17:50:58 +0100 | [diff] [blame] | 13 | import warnings | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 14 |  | 
 | 15 |  | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 16 | from . import base_events | 
| Guido van Rossum | 5969128 | 2013-10-30 14:52:03 -0700 | [diff] [blame] | 17 | from . import base_subprocess | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 18 | from . import constants | 
| Guido van Rossum | e36fcde | 2014-11-14 11:45:47 -0800 | [diff] [blame] | 19 | from . import coroutines | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 20 | from . import events | 
| Victor Stinner | 47cd10d | 2015-01-30 00:05:19 +0100 | [diff] [blame] | 21 | from . import futures | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 22 | from . import selector_events | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 23 | from . import transports | 
| Guido van Rossum | fc29e0f | 2013-10-17 15:39:45 -0700 | [diff] [blame] | 24 | from .log import logger | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 25 |  | 
 | 26 |  | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 27 | __all__ = ( | 
 | 28 |     'SelectorEventLoop', | 
 | 29 |     'AbstractChildWatcher', 'SafeChildWatcher', | 
 | 30 |     'FastChildWatcher', 'DefaultEventLoopPolicy', | 
 | 31 | ) | 
 | 32 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 33 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 34 | if sys.platform == 'win32':  # pragma: no cover | 
 | 35 |     raise ImportError('Signals are not really supported on Windows') | 
 | 36 |  | 
 | 37 |  | 
| Victor Stinner | fe5649c | 2014-07-17 22:43:40 +0200 | [diff] [blame] | 38 | def _sighandler_noop(signum, frame): | 
 | 39 |     """Dummy signal handler.""" | 
 | 40 |     pass | 
 | 41 |  | 
 | 42 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 43 | class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 44 |     """Unix event loop. | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 45 |  | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 46 |     Adds signal handling and UNIX Domain Socket support to SelectorEventLoop. | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 47 |     """ | 
 | 48 |  | 
 | 49 |     def __init__(self, selector=None): | 
 | 50 |         super().__init__(selector) | 
 | 51 |         self._signal_handlers = {} | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 52 |  | 
| Guido van Rossum | 0b69fbc | 2013-11-06 20:25:50 -0800 | [diff] [blame] | 53 |     def close(self): | 
| Victor Stinner | f328c7d | 2014-06-23 01:02:37 +0200 | [diff] [blame] | 54 |         super().close() | 
| Andrew Svetlov | 4a02543 | 2017-12-21 17:06:46 +0200 | [diff] [blame] | 55 |         if not sys.is_finalizing(): | 
 | 56 |             for sig in list(self._signal_handlers): | 
 | 57 |                 self.remove_signal_handler(sig) | 
 | 58 |         else: | 
| Andrew Svetlov | 4f146f9 | 2017-12-24 13:50:03 +0200 | [diff] [blame] | 59 |             if self._signal_handlers: | 
| Andrew Svetlov | a8f4e15 | 2017-12-26 11:53:38 +0200 | [diff] [blame] | 60 |                 warnings.warn(f"Closing the loop {self!r} " | 
| Andrew Svetlov | 4f146f9 | 2017-12-24 13:50:03 +0200 | [diff] [blame] | 61 |                               f"on interpreter shutdown " | 
| Andrew Svetlov | a8f4e15 | 2017-12-26 11:53:38 +0200 | [diff] [blame] | 62 |                               f"stage, skipping signal handlers removal", | 
| Andrew Svetlov | 4f146f9 | 2017-12-24 13:50:03 +0200 | [diff] [blame] | 63 |                               ResourceWarning, | 
 | 64 |                               source=self) | 
 | 65 |                 self._signal_handlers.clear() | 
| Guido van Rossum | 0b69fbc | 2013-11-06 20:25:50 -0800 | [diff] [blame] | 66 |  | 
| Victor Stinner | fe5649c | 2014-07-17 22:43:40 +0200 | [diff] [blame] | 67 |     def _process_self_data(self, data): | 
 | 68 |         for signum in data: | 
 | 69 |             if not signum: | 
 | 70 |                 # ignore null bytes written by _write_to_self() | 
 | 71 |                 continue | 
 | 72 |             self._handle_signal(signum) | 
 | 73 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 74 |     def add_signal_handler(self, sig, callback, *args): | 
 | 75 |         """Add a handler for a signal.  UNIX only. | 
 | 76 |  | 
 | 77 |         Raise ValueError if the signal number is invalid or uncatchable. | 
 | 78 |         Raise RuntimeError if there is a problem setting up the handler. | 
 | 79 |         """ | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 80 |         if (coroutines.iscoroutine(callback) or | 
 | 81 |                 coroutines.iscoroutinefunction(callback)): | 
| Victor Stinner | 15cc678 | 2015-01-09 00:09:10 +0100 | [diff] [blame] | 82 |             raise TypeError("coroutines cannot be used " | 
 | 83 |                             "with add_signal_handler()") | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 84 |         self._check_signal(sig) | 
| Victor Stinner | e80bf0d | 2014-12-04 23:07:47 +0100 | [diff] [blame] | 85 |         self._check_closed() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 86 |         try: | 
 | 87 |             # set_wakeup_fd() raises ValueError if this is not the | 
 | 88 |             # main thread.  By calling it early we ensure that an | 
 | 89 |             # event loop running in another thread cannot add a signal | 
 | 90 |             # handler. | 
 | 91 |             signal.set_wakeup_fd(self._csock.fileno()) | 
| Victor Stinner | c4c4649 | 2014-07-23 18:21:45 +0200 | [diff] [blame] | 92 |         except (ValueError, OSError) as exc: | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 93 |             raise RuntimeError(str(exc)) | 
 | 94 |  | 
| Yury Selivanov | f23746a | 2018-01-22 19:11:18 -0500 | [diff] [blame] | 95 |         handle = events.Handle(callback, args, self, None) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 96 |         self._signal_handlers[sig] = handle | 
 | 97 |  | 
 | 98 |         try: | 
| Victor Stinner | fe5649c | 2014-07-17 22:43:40 +0200 | [diff] [blame] | 99 |             # Register a dummy signal handler to ask Python to write the signal | 
 | 100 |             # number in the wakup file descriptor. _process_self_data() will | 
 | 101 |             # read signal numbers from this file descriptor to handle signals. | 
 | 102 |             signal.signal(sig, _sighandler_noop) | 
 | 103 |  | 
| Charles-François Natali | 74e7cf3 | 2013-12-05 22:47:19 +0100 | [diff] [blame] | 104 |             # Set SA_RESTART to limit EINTR occurrences. | 
 | 105 |             signal.siginterrupt(sig, False) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 106 |         except OSError as exc: | 
 | 107 |             del self._signal_handlers[sig] | 
 | 108 |             if not self._signal_handlers: | 
 | 109 |                 try: | 
 | 110 |                     signal.set_wakeup_fd(-1) | 
| Victor Stinner | c4c4649 | 2014-07-23 18:21:45 +0200 | [diff] [blame] | 111 |                 except (ValueError, OSError) as nexc: | 
| Guido van Rossum | fc29e0f | 2013-10-17 15:39:45 -0700 | [diff] [blame] | 112 |                     logger.info('set_wakeup_fd(-1) failed: %s', nexc) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 113 |  | 
 | 114 |             if exc.errno == errno.EINVAL: | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 115 |                 raise RuntimeError(f'sig {sig} cannot be caught') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 116 |             else: | 
 | 117 |                 raise | 
 | 118 |  | 
| Victor Stinner | fe5649c | 2014-07-17 22:43:40 +0200 | [diff] [blame] | 119 |     def _handle_signal(self, sig): | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 120 |         """Internal helper that is the actual signal handler.""" | 
 | 121 |         handle = self._signal_handlers.get(sig) | 
 | 122 |         if handle is None: | 
 | 123 |             return  # Assume it's some race condition. | 
 | 124 |         if handle._cancelled: | 
 | 125 |             self.remove_signal_handler(sig)  # Remove it properly. | 
 | 126 |         else: | 
 | 127 |             self._add_callback_signalsafe(handle) | 
 | 128 |  | 
 | 129 |     def remove_signal_handler(self, sig): | 
 | 130 |         """Remove a handler for a signal.  UNIX only. | 
 | 131 |  | 
 | 132 |         Return True if a signal handler was removed, False if not. | 
 | 133 |         """ | 
 | 134 |         self._check_signal(sig) | 
 | 135 |         try: | 
 | 136 |             del self._signal_handlers[sig] | 
 | 137 |         except KeyError: | 
 | 138 |             return False | 
 | 139 |  | 
 | 140 |         if sig == signal.SIGINT: | 
 | 141 |             handler = signal.default_int_handler | 
 | 142 |         else: | 
 | 143 |             handler = signal.SIG_DFL | 
 | 144 |  | 
 | 145 |         try: | 
 | 146 |             signal.signal(sig, handler) | 
 | 147 |         except OSError as exc: | 
 | 148 |             if exc.errno == errno.EINVAL: | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 149 |                 raise RuntimeError(f'sig {sig} cannot be caught') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 150 |             else: | 
 | 151 |                 raise | 
 | 152 |  | 
 | 153 |         if not self._signal_handlers: | 
 | 154 |             try: | 
 | 155 |                 signal.set_wakeup_fd(-1) | 
| Victor Stinner | c4c4649 | 2014-07-23 18:21:45 +0200 | [diff] [blame] | 156 |             except (ValueError, OSError) as exc: | 
| Guido van Rossum | fc29e0f | 2013-10-17 15:39:45 -0700 | [diff] [blame] | 157 |                 logger.info('set_wakeup_fd(-1) failed: %s', exc) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 158 |  | 
 | 159 |         return True | 
 | 160 |  | 
 | 161 |     def _check_signal(self, sig): | 
 | 162 |         """Internal helper to validate a signal. | 
 | 163 |  | 
 | 164 |         Raise ValueError if the signal number is invalid or uncatchable. | 
 | 165 |         Raise RuntimeError if there is a problem setting up the handler. | 
 | 166 |         """ | 
 | 167 |         if not isinstance(sig, int): | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 168 |             raise TypeError(f'sig must be an int, not {sig!r}') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 169 |  | 
| Antoine Pitrou | 9d3627e | 2018-05-04 13:00:50 +0200 | [diff] [blame^] | 170 |         if sig not in signal.valid_signals(): | 
 | 171 |             raise ValueError(f'invalid signal number {sig}') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 172 |  | 
 | 173 |     def _make_read_pipe_transport(self, pipe, protocol, waiter=None, | 
 | 174 |                                   extra=None): | 
 | 175 |         return _UnixReadPipeTransport(self, pipe, protocol, waiter, extra) | 
 | 176 |  | 
 | 177 |     def _make_write_pipe_transport(self, pipe, protocol, waiter=None, | 
 | 178 |                                    extra=None): | 
 | 179 |         return _UnixWritePipeTransport(self, pipe, protocol, waiter, extra) | 
 | 180 |  | 
| Andrew Svetlov | 5f841b5 | 2017-12-09 00:23:48 +0200 | [diff] [blame] | 181 |     async def _make_subprocess_transport(self, protocol, args, shell, | 
 | 182 |                                          stdin, stdout, stderr, bufsize, | 
 | 183 |                                          extra=None, **kwargs): | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 184 |         with events.get_child_watcher() as watcher: | 
| Yury Selivanov | 7661db6 | 2016-05-16 15:38:39 -0400 | [diff] [blame] | 185 |             waiter = self.create_future() | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 186 |             transp = _UnixSubprocessTransport(self, protocol, args, shell, | 
 | 187 |                                               stdin, stdout, stderr, bufsize, | 
| Victor Stinner | 47cd10d | 2015-01-30 00:05:19 +0100 | [diff] [blame] | 188 |                                               waiter=waiter, extra=extra, | 
 | 189 |                                               **kwargs) | 
 | 190 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 191 |             watcher.add_child_handler(transp.get_pid(), | 
 | 192 |                                       self._child_watcher_callback, transp) | 
| Victor Stinner | 47cd10d | 2015-01-30 00:05:19 +0100 | [diff] [blame] | 193 |             try: | 
| Andrew Svetlov | 5f841b5 | 2017-12-09 00:23:48 +0200 | [diff] [blame] | 194 |                 await waiter | 
 | 195 |             except Exception: | 
| Victor Stinner | 47cd10d | 2015-01-30 00:05:19 +0100 | [diff] [blame] | 196 |                 transp.close() | 
| Andrew Svetlov | 5f841b5 | 2017-12-09 00:23:48 +0200 | [diff] [blame] | 197 |                 await transp._wait() | 
 | 198 |                 raise | 
| Guido van Rossum | 4835f17 | 2014-01-10 13:28:59 -0800 | [diff] [blame] | 199 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 200 |         return transp | 
 | 201 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 202 |     def _child_watcher_callback(self, pid, returncode, transp): | 
 | 203 |         self.call_soon_threadsafe(transp._process_exited, returncode) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 204 |  | 
| Neil Aspinall | f7686c1 | 2017-12-19 19:45:42 +0000 | [diff] [blame] | 205 |     async def create_unix_connection( | 
 | 206 |             self, protocol_factory, path=None, *, | 
 | 207 |             ssl=None, sock=None, | 
 | 208 |             server_hostname=None, | 
| Andrew Svetlov | 51eb1c6 | 2017-12-20 20:24:43 +0200 | [diff] [blame] | 209 |             ssl_handshake_timeout=None): | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 210 |         assert server_hostname is None or isinstance(server_hostname, str) | 
 | 211 |         if ssl: | 
 | 212 |             if server_hostname is None: | 
 | 213 |                 raise ValueError( | 
 | 214 |                     'you have to pass server_hostname when using ssl') | 
 | 215 |         else: | 
 | 216 |             if server_hostname is not None: | 
 | 217 |                 raise ValueError('server_hostname is only meaningful with ssl') | 
| Andrew Svetlov | 51eb1c6 | 2017-12-20 20:24:43 +0200 | [diff] [blame] | 218 |             if ssl_handshake_timeout is not None: | 
 | 219 |                 raise ValueError( | 
 | 220 |                     'ssl_handshake_timeout is only meaningful with ssl') | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 221 |  | 
 | 222 |         if path is not None: | 
 | 223 |             if sock is not None: | 
 | 224 |                 raise ValueError( | 
 | 225 |                     'path and sock can not be specified at the same time') | 
 | 226 |  | 
| Andrew Svetlov | cc83920 | 2017-11-29 18:23:43 +0200 | [diff] [blame] | 227 |             path = os.fspath(path) | 
| Victor Stinner | 79a2952 | 2014-02-19 01:45:59 +0100 | [diff] [blame] | 228 |             sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 229 |             try: | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 230 |                 sock.setblocking(False) | 
| Andrew Svetlov | 5f841b5 | 2017-12-09 00:23:48 +0200 | [diff] [blame] | 231 |                 await self.sock_connect(sock, path) | 
| Victor Stinner | 79a2952 | 2014-02-19 01:45:59 +0100 | [diff] [blame] | 232 |             except: | 
 | 233 |                 sock.close() | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 234 |                 raise | 
 | 235 |  | 
 | 236 |         else: | 
 | 237 |             if sock is None: | 
 | 238 |                 raise ValueError('no path and sock were specified') | 
| Yury Selivanov | 36e7e97 | 2016-10-07 12:39:57 -0400 | [diff] [blame] | 239 |             if (sock.family != socket.AF_UNIX or | 
| Yury Selivanov | a7bd64c | 2017-12-19 06:44:37 -0500 | [diff] [blame] | 240 |                     sock.type != socket.SOCK_STREAM): | 
| Yury Selivanov | 36e7e97 | 2016-10-07 12:39:57 -0400 | [diff] [blame] | 241 |                 raise ValueError( | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 242 |                     f'A UNIX Domain Stream Socket was expected, got {sock!r}') | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 243 |             sock.setblocking(False) | 
 | 244 |  | 
| Andrew Svetlov | 5f841b5 | 2017-12-09 00:23:48 +0200 | [diff] [blame] | 245 |         transport, protocol = await self._create_connection_transport( | 
| Neil Aspinall | f7686c1 | 2017-12-19 19:45:42 +0000 | [diff] [blame] | 246 |             sock, protocol_factory, ssl, server_hostname, | 
 | 247 |             ssl_handshake_timeout=ssl_handshake_timeout) | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 248 |         return transport, protocol | 
 | 249 |  | 
| Neil Aspinall | f7686c1 | 2017-12-19 19:45:42 +0000 | [diff] [blame] | 250 |     async def create_unix_server( | 
 | 251 |             self, protocol_factory, path=None, *, | 
 | 252 |             sock=None, backlog=100, ssl=None, | 
| Yury Selivanov | c9070d0 | 2018-01-25 18:08:09 -0500 | [diff] [blame] | 253 |             ssl_handshake_timeout=None, | 
 | 254 |             start_serving=True): | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 255 |         if isinstance(ssl, bool): | 
 | 256 |             raise TypeError('ssl argument must be an SSLContext or None') | 
 | 257 |  | 
| Andrew Svetlov | 51eb1c6 | 2017-12-20 20:24:43 +0200 | [diff] [blame] | 258 |         if ssl_handshake_timeout is not None and not ssl: | 
 | 259 |             raise ValueError( | 
 | 260 |                 'ssl_handshake_timeout is only meaningful with ssl') | 
 | 261 |  | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 262 |         if path is not None: | 
| Victor Stinner | 1fd03a4 | 2014-04-07 11:18:54 +0200 | [diff] [blame] | 263 |             if sock is not None: | 
 | 264 |                 raise ValueError( | 
 | 265 |                     'path and sock can not be specified at the same time') | 
 | 266 |  | 
| Andrew Svetlov | cc83920 | 2017-11-29 18:23:43 +0200 | [diff] [blame] | 267 |             path = os.fspath(path) | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 268 |             sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 
 | 269 |  | 
| Yury Selivanov | 908d55d | 2016-10-09 12:15:08 -0400 | [diff] [blame] | 270 |             # Check for abstract socket. `str` and `bytes` paths are supported. | 
 | 271 |             if path[0] not in (0, '\x00'): | 
 | 272 |                 try: | 
 | 273 |                     if stat.S_ISSOCK(os.stat(path).st_mode): | 
 | 274 |                         os.remove(path) | 
 | 275 |                 except FileNotFoundError: | 
 | 276 |                     pass | 
 | 277 |                 except OSError as err: | 
 | 278 |                     # Directory may have permissions only to create socket. | 
| Andrew Svetlov | cc83920 | 2017-11-29 18:23:43 +0200 | [diff] [blame] | 279 |                     logger.error('Unable to check or remove stale UNIX socket ' | 
 | 280 |                                  '%r: %r', path, err) | 
| Yury Selivanov | 908d55d | 2016-10-09 12:15:08 -0400 | [diff] [blame] | 281 |  | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 282 |             try: | 
 | 283 |                 sock.bind(path) | 
 | 284 |             except OSError as exc: | 
| Victor Stinner | 79a2952 | 2014-02-19 01:45:59 +0100 | [diff] [blame] | 285 |                 sock.close() | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 286 |                 if exc.errno == errno.EADDRINUSE: | 
 | 287 |                     # Let's improve the error message by adding | 
 | 288 |                     # with what exact address it occurs. | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 289 |                     msg = f'Address {path!r} is already in use' | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 290 |                     raise OSError(errno.EADDRINUSE, msg) from None | 
 | 291 |                 else: | 
 | 292 |                     raise | 
| Victor Stinner | 223a624 | 2014-06-04 00:11:52 +0200 | [diff] [blame] | 293 |             except: | 
 | 294 |                 sock.close() | 
 | 295 |                 raise | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 296 |         else: | 
 | 297 |             if sock is None: | 
 | 298 |                 raise ValueError( | 
 | 299 |                     'path was not specified, and no sock specified') | 
 | 300 |  | 
| Yury Selivanov | 36e7e97 | 2016-10-07 12:39:57 -0400 | [diff] [blame] | 301 |             if (sock.family != socket.AF_UNIX or | 
| Yury Selivanov | a7bd64c | 2017-12-19 06:44:37 -0500 | [diff] [blame] | 302 |                     sock.type != socket.SOCK_STREAM): | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 303 |                 raise ValueError( | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 304 |                     f'A UNIX Domain Stream Socket was expected, got {sock!r}') | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 305 |  | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 306 |         sock.setblocking(False) | 
| Yury Selivanov | c9070d0 | 2018-01-25 18:08:09 -0500 | [diff] [blame] | 307 |         server = base_events.Server(self, [sock], protocol_factory, | 
 | 308 |                                     ssl, backlog, ssl_handshake_timeout) | 
 | 309 |         if start_serving: | 
 | 310 |             server._start_serving() | 
 | 311 |  | 
| Yury Selivanov | b057c52 | 2014-02-18 12:15:06 -0500 | [diff] [blame] | 312 |         return server | 
 | 313 |  | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 314 |     async def _sock_sendfile_native(self, sock, file, offset, count): | 
 | 315 |         try: | 
 | 316 |             os.sendfile | 
 | 317 |         except AttributeError as exc: | 
| Andrew Svetlov | 7464e87 | 2018-01-19 20:04:29 +0200 | [diff] [blame] | 318 |             raise events.SendfileNotAvailableError( | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 319 |                 "os.sendfile() is not available") | 
 | 320 |         try: | 
 | 321 |             fileno = file.fileno() | 
 | 322 |         except (AttributeError, io.UnsupportedOperation) as err: | 
| Andrew Svetlov | 7464e87 | 2018-01-19 20:04:29 +0200 | [diff] [blame] | 323 |             raise events.SendfileNotAvailableError("not a regular file") | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 324 |         try: | 
 | 325 |             fsize = os.fstat(fileno).st_size | 
 | 326 |         except OSError as err: | 
| Andrew Svetlov | 7464e87 | 2018-01-19 20:04:29 +0200 | [diff] [blame] | 327 |             raise events.SendfileNotAvailableError("not a regular file") | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 328 |         blocksize = count if count else fsize | 
 | 329 |         if not blocksize: | 
 | 330 |             return 0  # empty file | 
 | 331 |  | 
 | 332 |         fut = self.create_future() | 
 | 333 |         self._sock_sendfile_native_impl(fut, None, sock, fileno, | 
 | 334 |                                         offset, count, blocksize, 0) | 
 | 335 |         return await fut | 
 | 336 |  | 
 | 337 |     def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno, | 
 | 338 |                                    offset, count, blocksize, total_sent): | 
 | 339 |         fd = sock.fileno() | 
 | 340 |         if registered_fd is not None: | 
 | 341 |             # Remove the callback early.  It should be rare that the | 
 | 342 |             # selector says the fd is ready but the call still returns | 
 | 343 |             # EAGAIN, and I am willing to take a hit in that case in | 
 | 344 |             # order to simplify the common case. | 
 | 345 |             self.remove_writer(registered_fd) | 
 | 346 |         if fut.cancelled(): | 
 | 347 |             self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
 | 348 |             return | 
 | 349 |         if count: | 
 | 350 |             blocksize = count - total_sent | 
 | 351 |             if blocksize <= 0: | 
 | 352 |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
 | 353 |                 fut.set_result(total_sent) | 
 | 354 |                 return | 
 | 355 |  | 
 | 356 |         try: | 
 | 357 |             sent = os.sendfile(fd, fileno, offset, blocksize) | 
 | 358 |         except (BlockingIOError, InterruptedError): | 
 | 359 |             if registered_fd is None: | 
 | 360 |                 self._sock_add_cancellation_callback(fut, sock) | 
 | 361 |             self.add_writer(fd, self._sock_sendfile_native_impl, fut, | 
 | 362 |                             fd, sock, fileno, | 
 | 363 |                             offset, count, blocksize, total_sent) | 
 | 364 |         except OSError as exc: | 
| Yury Selivanov | 2a2247c | 2018-01-27 17:22:01 -0500 | [diff] [blame] | 365 |             if (registered_fd is not None and | 
 | 366 |                     exc.errno == errno.ENOTCONN and | 
 | 367 |                     type(exc) is not ConnectionError): | 
 | 368 |                 # If we have an ENOTCONN and this isn't a first call to | 
 | 369 |                 # sendfile(), i.e. the connection was closed in the middle | 
 | 370 |                 # of the operation, normalize the error to ConnectionError | 
 | 371 |                 # to make it consistent across all Posix systems. | 
 | 372 |                 new_exc = ConnectionError( | 
 | 373 |                     "socket is not connected", errno.ENOTCONN) | 
 | 374 |                 new_exc.__cause__ = exc | 
 | 375 |                 exc = new_exc | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 376 |             if total_sent == 0: | 
 | 377 |                 # We can get here for different reasons, the main | 
 | 378 |                 # one being 'file' is not a regular mmap(2)-like | 
 | 379 |                 # file, in which case we'll fall back on using | 
 | 380 |                 # plain send(). | 
| Andrew Svetlov | 7464e87 | 2018-01-19 20:04:29 +0200 | [diff] [blame] | 381 |                 err = events.SendfileNotAvailableError( | 
| Andrew Svetlov | 6b5a279 | 2018-01-16 19:59:34 +0200 | [diff] [blame] | 382 |                     "os.sendfile call failed") | 
 | 383 |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
 | 384 |                 fut.set_exception(err) | 
 | 385 |             else: | 
 | 386 |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
 | 387 |                 fut.set_exception(exc) | 
 | 388 |         except Exception as exc: | 
 | 389 |             self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
 | 390 |             fut.set_exception(exc) | 
 | 391 |         else: | 
 | 392 |             if sent == 0: | 
 | 393 |                 # EOF | 
 | 394 |                 self._sock_sendfile_update_filepos(fileno, offset, total_sent) | 
 | 395 |                 fut.set_result(total_sent) | 
 | 396 |             else: | 
 | 397 |                 offset += sent | 
 | 398 |                 total_sent += sent | 
 | 399 |                 if registered_fd is None: | 
 | 400 |                     self._sock_add_cancellation_callback(fut, sock) | 
 | 401 |                 self.add_writer(fd, self._sock_sendfile_native_impl, fut, | 
 | 402 |                                 fd, sock, fileno, | 
 | 403 |                                 offset, count, blocksize, total_sent) | 
 | 404 |  | 
 | 405 |     def _sock_sendfile_update_filepos(self, fileno, offset, total_sent): | 
 | 406 |         if total_sent > 0: | 
 | 407 |             os.lseek(fileno, offset, os.SEEK_SET) | 
 | 408 |  | 
 | 409 |     def _sock_add_cancellation_callback(self, fut, sock): | 
 | 410 |         def cb(fut): | 
 | 411 |             if fut.cancelled(): | 
 | 412 |                 fd = sock.fileno() | 
 | 413 |                 if fd != -1: | 
 | 414 |                     self.remove_writer(fd) | 
 | 415 |         fut.add_done_callback(cb) | 
 | 416 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 417 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 418 | class _UnixReadPipeTransport(transports.ReadTransport): | 
 | 419 |  | 
| Yury Selivanov | dec1a45 | 2014-02-18 22:27:48 -0500 | [diff] [blame] | 420 |     max_size = 256 * 1024  # max bytes we read in one event loop iteration | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 421 |  | 
 | 422 |     def __init__(self, loop, pipe, protocol, waiter=None, extra=None): | 
 | 423 |         super().__init__(extra) | 
 | 424 |         self._extra['pipe'] = pipe | 
 | 425 |         self._loop = loop | 
 | 426 |         self._pipe = pipe | 
 | 427 |         self._fileno = pipe.fileno() | 
| Guido van Rossum | 4786787 | 2016-08-31 09:42:38 -0700 | [diff] [blame] | 428 |         self._protocol = protocol | 
 | 429 |         self._closing = False | 
 | 430 |  | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 431 |         mode = os.fstat(self._fileno).st_mode | 
| Guido van Rossum | 02757ea | 2014-01-10 13:30:04 -0800 | [diff] [blame] | 432 |         if not (stat.S_ISFIFO(mode) or | 
 | 433 |                 stat.S_ISSOCK(mode) or | 
 | 434 |                 stat.S_ISCHR(mode)): | 
| Guido van Rossum | 4786787 | 2016-08-31 09:42:38 -0700 | [diff] [blame] | 435 |             self._pipe = None | 
 | 436 |             self._fileno = None | 
 | 437 |             self._protocol = None | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 438 |             raise ValueError("Pipe transport is for pipes/sockets only.") | 
| Guido van Rossum | 4786787 | 2016-08-31 09:42:38 -0700 | [diff] [blame] | 439 |  | 
| Andrew Svetlov | cc83920 | 2017-11-29 18:23:43 +0200 | [diff] [blame] | 440 |         os.set_blocking(self._fileno, False) | 
| Guido van Rossum | 4786787 | 2016-08-31 09:42:38 -0700 | [diff] [blame] | 441 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 442 |         self._loop.call_soon(self._protocol.connection_made, self) | 
| Victor Stinner | 2934262 | 2015-01-29 14:15:19 +0100 | [diff] [blame] | 443 |         # only start reading when connection_made() has been called | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 444 |         self._loop.call_soon(self._loop._add_reader, | 
| Victor Stinner | 2934262 | 2015-01-29 14:15:19 +0100 | [diff] [blame] | 445 |                              self._fileno, self._read_ready) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 446 |         if waiter is not None: | 
| Victor Stinner | f07801b | 2015-01-29 00:36:35 +0100 | [diff] [blame] | 447 |             # only wake up the waiter when connection_made() has been called | 
| Yury Selivanov | 5d7e3b6 | 2015-11-17 12:19:41 -0500 | [diff] [blame] | 448 |             self._loop.call_soon(futures._set_result_unless_cancelled, | 
 | 449 |                                  waiter, None) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 450 |  | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 451 |     def __repr__(self): | 
| Victor Stinner | 29ad011 | 2015-01-15 00:04:21 +0100 | [diff] [blame] | 452 |         info = [self.__class__.__name__] | 
 | 453 |         if self._pipe is None: | 
 | 454 |             info.append('closed') | 
 | 455 |         elif self._closing: | 
 | 456 |             info.append('closing') | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 457 |         info.append(f'fd={self._fileno}') | 
| Yury Selivanov | 5dc0933 | 2016-05-13 16:04:43 -0400 | [diff] [blame] | 458 |         selector = getattr(self._loop, '_selector', None) | 
 | 459 |         if self._pipe is not None and selector is not None: | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 460 |             polling = selector_events._test_selector_event( | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 461 |                 selector, self._fileno, selectors.EVENT_READ) | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 462 |             if polling: | 
 | 463 |                 info.append('polling') | 
 | 464 |             else: | 
 | 465 |                 info.append('idle') | 
| Yury Selivanov | 5dc0933 | 2016-05-13 16:04:43 -0400 | [diff] [blame] | 466 |         elif self._pipe is not None: | 
 | 467 |             info.append('open') | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 468 |         else: | 
 | 469 |             info.append('closed') | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 470 |         return '<{}>'.format(' '.join(info)) | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 471 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 472 |     def _read_ready(self): | 
 | 473 |         try: | 
 | 474 |             data = os.read(self._fileno, self.max_size) | 
 | 475 |         except (BlockingIOError, InterruptedError): | 
 | 476 |             pass | 
 | 477 |         except OSError as exc: | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 478 |             self._fatal_error(exc, 'Fatal read error on pipe transport') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 479 |         else: | 
 | 480 |             if data: | 
 | 481 |                 self._protocol.data_received(data) | 
 | 482 |             else: | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 483 |                 if self._loop.get_debug(): | 
 | 484 |                     logger.info("%r was closed by peer", self) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 485 |                 self._closing = True | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 486 |                 self._loop._remove_reader(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 487 |                 self._loop.call_soon(self._protocol.eof_received) | 
 | 488 |                 self._loop.call_soon(self._call_connection_lost, None) | 
 | 489 |  | 
| Guido van Rossum | 57497ad | 2013-10-18 07:58:20 -0700 | [diff] [blame] | 490 |     def pause_reading(self): | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 491 |         self._loop._remove_reader(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 492 |  | 
| Guido van Rossum | 57497ad | 2013-10-18 07:58:20 -0700 | [diff] [blame] | 493 |     def resume_reading(self): | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 494 |         self._loop._add_reader(self._fileno, self._read_ready) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 495 |  | 
| Yury Selivanov | a05a6ef | 2016-09-11 21:11:02 -0400 | [diff] [blame] | 496 |     def set_protocol(self, protocol): | 
 | 497 |         self._protocol = protocol | 
 | 498 |  | 
 | 499 |     def get_protocol(self): | 
 | 500 |         return self._protocol | 
 | 501 |  | 
| Yury Selivanov | 5bb1afb | 2015-11-16 12:43:21 -0500 | [diff] [blame] | 502 |     def is_closing(self): | 
 | 503 |         return self._closing | 
 | 504 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 505 |     def close(self): | 
 | 506 |         if not self._closing: | 
 | 507 |             self._close(None) | 
 | 508 |  | 
| INADA Naoki | 3e2ad8e | 2017-04-25 10:57:18 +0900 | [diff] [blame] | 509 |     def __del__(self): | 
 | 510 |         if self._pipe is not None: | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 511 |             warnings.warn(f"unclosed transport {self!r}", ResourceWarning, | 
| INADA Naoki | 3e2ad8e | 2017-04-25 10:57:18 +0900 | [diff] [blame] | 512 |                           source=self) | 
 | 513 |             self._pipe.close() | 
| Victor Stinner | 978a9af | 2015-01-29 17:50:58 +0100 | [diff] [blame] | 514 |  | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 515 |     def _fatal_error(self, exc, message='Fatal error on pipe transport'): | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 516 |         # should be called by exception handler only | 
| Victor Stinner | b261475 | 2014-08-25 23:20:52 +0200 | [diff] [blame] | 517 |         if (isinstance(exc, OSError) and exc.errno == errno.EIO): | 
 | 518 |             if self._loop.get_debug(): | 
 | 519 |                 logger.debug("%r: %s", self, message, exc_info=True) | 
 | 520 |         else: | 
| Yury Selivanov | 569efa2 | 2014-02-18 18:02:19 -0500 | [diff] [blame] | 521 |             self._loop.call_exception_handler({ | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 522 |                 'message': message, | 
| Yury Selivanov | 569efa2 | 2014-02-18 18:02:19 -0500 | [diff] [blame] | 523 |                 'exception': exc, | 
 | 524 |                 'transport': self, | 
 | 525 |                 'protocol': self._protocol, | 
 | 526 |             }) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 527 |         self._close(exc) | 
 | 528 |  | 
 | 529 |     def _close(self, exc): | 
 | 530 |         self._closing = True | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 531 |         self._loop._remove_reader(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 532 |         self._loop.call_soon(self._call_connection_lost, exc) | 
 | 533 |  | 
 | 534 |     def _call_connection_lost(self, exc): | 
 | 535 |         try: | 
 | 536 |             self._protocol.connection_lost(exc) | 
 | 537 |         finally: | 
 | 538 |             self._pipe.close() | 
 | 539 |             self._pipe = None | 
 | 540 |             self._protocol = None | 
 | 541 |             self._loop = None | 
 | 542 |  | 
 | 543 |  | 
| Yury Selivanov | 3cb9914 | 2014-02-18 18:41:13 -0500 | [diff] [blame] | 544 | class _UnixWritePipeTransport(transports._FlowControlMixin, | 
| Guido van Rossum | 47fb97e | 2014-01-29 13:20:39 -0800 | [diff] [blame] | 545 |                               transports.WriteTransport): | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 546 |  | 
 | 547 |     def __init__(self, loop, pipe, protocol, waiter=None, extra=None): | 
| Victor Stinner | 004adb9 | 2014-11-05 15:27:41 +0100 | [diff] [blame] | 548 |         super().__init__(extra, loop) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 549 |         self._extra['pipe'] = pipe | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 550 |         self._pipe = pipe | 
 | 551 |         self._fileno = pipe.fileno() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 552 |         self._protocol = protocol | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 553 |         self._buffer = bytearray() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 554 |         self._conn_lost = 0 | 
 | 555 |         self._closing = False  # Set when close() or write_eof() called. | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 556 |  | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 557 |         mode = os.fstat(self._fileno).st_mode | 
| Guido van Rossum | 8b7918a | 2016-08-31 09:40:18 -0700 | [diff] [blame] | 558 |         is_char = stat.S_ISCHR(mode) | 
 | 559 |         is_fifo = stat.S_ISFIFO(mode) | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 560 |         is_socket = stat.S_ISSOCK(mode) | 
| Guido van Rossum | 8b7918a | 2016-08-31 09:40:18 -0700 | [diff] [blame] | 561 |         if not (is_char or is_fifo or is_socket): | 
| Guido van Rossum | 4786787 | 2016-08-31 09:42:38 -0700 | [diff] [blame] | 562 |             self._pipe = None | 
 | 563 |             self._fileno = None | 
 | 564 |             self._protocol = None | 
| Victor Stinner | 8dffc45 | 2014-01-25 15:32:06 +0100 | [diff] [blame] | 565 |             raise ValueError("Pipe transport is only for " | 
 | 566 |                              "pipes, sockets and character devices") | 
| Guido van Rossum | 4786787 | 2016-08-31 09:42:38 -0700 | [diff] [blame] | 567 |  | 
| Andrew Svetlov | cc83920 | 2017-11-29 18:23:43 +0200 | [diff] [blame] | 568 |         os.set_blocking(self._fileno, False) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 569 |         self._loop.call_soon(self._protocol.connection_made, self) | 
| Victor Stinner | 2934262 | 2015-01-29 14:15:19 +0100 | [diff] [blame] | 570 |  | 
 | 571 |         # On AIX, the reader trick (to be notified when the read end of the | 
 | 572 |         # socket is closed) only works for sockets. On other platforms it | 
 | 573 |         # works for pipes and sockets. (Exception: OS X 10.4?  Issue #19294.) | 
| Guido van Rossum | 8b7918a | 2016-08-31 09:40:18 -0700 | [diff] [blame] | 574 |         if is_socket or (is_fifo and not sys.platform.startswith("aix")): | 
| Victor Stinner | 2934262 | 2015-01-29 14:15:19 +0100 | [diff] [blame] | 575 |             # only start reading when connection_made() has been called | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 576 |             self._loop.call_soon(self._loop._add_reader, | 
| Victor Stinner | 2934262 | 2015-01-29 14:15:19 +0100 | [diff] [blame] | 577 |                                  self._fileno, self._read_ready) | 
 | 578 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 579 |         if waiter is not None: | 
| Victor Stinner | f07801b | 2015-01-29 00:36:35 +0100 | [diff] [blame] | 580 |             # only wake up the waiter when connection_made() has been called | 
| Yury Selivanov | 5d7e3b6 | 2015-11-17 12:19:41 -0500 | [diff] [blame] | 581 |             self._loop.call_soon(futures._set_result_unless_cancelled, | 
 | 582 |                                  waiter, None) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 583 |  | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 584 |     def __repr__(self): | 
| Victor Stinner | 29ad011 | 2015-01-15 00:04:21 +0100 | [diff] [blame] | 585 |         info = [self.__class__.__name__] | 
 | 586 |         if self._pipe is None: | 
 | 587 |             info.append('closed') | 
 | 588 |         elif self._closing: | 
 | 589 |             info.append('closing') | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 590 |         info.append(f'fd={self._fileno}') | 
| Yury Selivanov | 5dc0933 | 2016-05-13 16:04:43 -0400 | [diff] [blame] | 591 |         selector = getattr(self._loop, '_selector', None) | 
 | 592 |         if self._pipe is not None and selector is not None: | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 593 |             polling = selector_events._test_selector_event( | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 594 |                 selector, self._fileno, selectors.EVENT_WRITE) | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 595 |             if polling: | 
 | 596 |                 info.append('polling') | 
 | 597 |             else: | 
 | 598 |                 info.append('idle') | 
 | 599 |  | 
 | 600 |             bufsize = self.get_write_buffer_size() | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 601 |             info.append(f'bufsize={bufsize}') | 
| Yury Selivanov | 5dc0933 | 2016-05-13 16:04:43 -0400 | [diff] [blame] | 602 |         elif self._pipe is not None: | 
 | 603 |             info.append('open') | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 604 |         else: | 
 | 605 |             info.append('closed') | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 606 |         return '<{}>'.format(' '.join(info)) | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 607 |  | 
| Guido van Rossum | 47fb97e | 2014-01-29 13:20:39 -0800 | [diff] [blame] | 608 |     def get_write_buffer_size(self): | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 609 |         return len(self._buffer) | 
| Guido van Rossum | 47fb97e | 2014-01-29 13:20:39 -0800 | [diff] [blame] | 610 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 611 |     def _read_ready(self): | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 612 |         # Pipe was closed by peer. | 
| Victor Stinner | e912e65 | 2014-07-12 03:11:53 +0200 | [diff] [blame] | 613 |         if self._loop.get_debug(): | 
 | 614 |             logger.info("%r was closed by peer", self) | 
| Victor Stinner | 61b3c9b | 2014-01-31 13:04:28 +0100 | [diff] [blame] | 615 |         if self._buffer: | 
 | 616 |             self._close(BrokenPipeError()) | 
 | 617 |         else: | 
 | 618 |             self._close() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 619 |  | 
 | 620 |     def write(self, data): | 
| Guido van Rossum | 47fb97e | 2014-01-29 13:20:39 -0800 | [diff] [blame] | 621 |         assert isinstance(data, (bytes, bytearray, memoryview)), repr(data) | 
 | 622 |         if isinstance(data, bytearray): | 
 | 623 |             data = memoryview(data) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 624 |         if not data: | 
 | 625 |             return | 
 | 626 |  | 
 | 627 |         if self._conn_lost or self._closing: | 
 | 628 |             if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES: | 
| Guido van Rossum | fc29e0f | 2013-10-17 15:39:45 -0700 | [diff] [blame] | 629 |                 logger.warning('pipe closed by peer or ' | 
 | 630 |                                'os.write(pipe, data) raised exception.') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 631 |             self._conn_lost += 1 | 
 | 632 |             return | 
 | 633 |  | 
 | 634 |         if not self._buffer: | 
 | 635 |             # Attempt to send it right away first. | 
 | 636 |             try: | 
 | 637 |                 n = os.write(self._fileno, data) | 
 | 638 |             except (BlockingIOError, InterruptedError): | 
 | 639 |                 n = 0 | 
 | 640 |             except Exception as exc: | 
 | 641 |                 self._conn_lost += 1 | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 642 |                 self._fatal_error(exc, 'Fatal write error on pipe transport') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 643 |                 return | 
 | 644 |             if n == len(data): | 
 | 645 |                 return | 
 | 646 |             elif n > 0: | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 647 |                 data = memoryview(data)[n:] | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 648 |             self._loop._add_writer(self._fileno, self._write_ready) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 649 |  | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 650 |         self._buffer += data | 
| Guido van Rossum | 47fb97e | 2014-01-29 13:20:39 -0800 | [diff] [blame] | 651 |         self._maybe_pause_protocol() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 652 |  | 
 | 653 |     def _write_ready(self): | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 654 |         assert self._buffer, 'Data should not be empty' | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 655 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 656 |         try: | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 657 |             n = os.write(self._fileno, self._buffer) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 658 |         except (BlockingIOError, InterruptedError): | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 659 |             pass | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 660 |         except Exception as exc: | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 661 |             self._buffer.clear() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 662 |             self._conn_lost += 1 | 
 | 663 |             # Remove writer here, _fatal_error() doesn't it | 
 | 664 |             # because _buffer is empty. | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 665 |             self._loop._remove_writer(self._fileno) | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 666 |             self._fatal_error(exc, 'Fatal write error on pipe transport') | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 667 |         else: | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 668 |             if n == len(self._buffer): | 
 | 669 |                 self._buffer.clear() | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 670 |                 self._loop._remove_writer(self._fileno) | 
| Guido van Rossum | 47fb97e | 2014-01-29 13:20:39 -0800 | [diff] [blame] | 671 |                 self._maybe_resume_protocol()  # May append to buffer. | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 672 |                 if self._closing: | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 673 |                     self._loop._remove_reader(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 674 |                     self._call_connection_lost(None) | 
 | 675 |                 return | 
 | 676 |             elif n > 0: | 
| Yury Selivanov | 4c5bf3b | 2016-09-15 16:51:48 -0400 | [diff] [blame] | 677 |                 del self._buffer[:n] | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 678 |  | 
 | 679 |     def can_write_eof(self): | 
 | 680 |         return True | 
 | 681 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 682 |     def write_eof(self): | 
 | 683 |         if self._closing: | 
 | 684 |             return | 
 | 685 |         assert self._pipe | 
 | 686 |         self._closing = True | 
 | 687 |         if not self._buffer: | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 688 |             self._loop._remove_reader(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 689 |             self._loop.call_soon(self._call_connection_lost, None) | 
 | 690 |  | 
| Yury Selivanov | a05a6ef | 2016-09-11 21:11:02 -0400 | [diff] [blame] | 691 |     def set_protocol(self, protocol): | 
 | 692 |         self._protocol = protocol | 
 | 693 |  | 
 | 694 |     def get_protocol(self): | 
 | 695 |         return self._protocol | 
 | 696 |  | 
| Yury Selivanov | 5bb1afb | 2015-11-16 12:43:21 -0500 | [diff] [blame] | 697 |     def is_closing(self): | 
 | 698 |         return self._closing | 
 | 699 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 700 |     def close(self): | 
| Victor Stinner | 41ed958 | 2015-01-15 13:16:50 +0100 | [diff] [blame] | 701 |         if self._pipe is not None and not self._closing: | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 702 |             # write_eof is all what we needed to close the write pipe | 
 | 703 |             self.write_eof() | 
 | 704 |  | 
| INADA Naoki | 3e2ad8e | 2017-04-25 10:57:18 +0900 | [diff] [blame] | 705 |     def __del__(self): | 
 | 706 |         if self._pipe is not None: | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 707 |             warnings.warn(f"unclosed transport {self!r}", ResourceWarning, | 
| INADA Naoki | 3e2ad8e | 2017-04-25 10:57:18 +0900 | [diff] [blame] | 708 |                           source=self) | 
 | 709 |             self._pipe.close() | 
| Victor Stinner | 978a9af | 2015-01-29 17:50:58 +0100 | [diff] [blame] | 710 |  | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 711 |     def abort(self): | 
 | 712 |         self._close(None) | 
 | 713 |  | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 714 |     def _fatal_error(self, exc, message='Fatal error on pipe transport'): | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 715 |         # should be called by exception handler only | 
| Victor Stinner | c94a93a | 2016-04-01 21:43:39 +0200 | [diff] [blame] | 716 |         if isinstance(exc, base_events._FATAL_ERROR_IGNORE): | 
| Victor Stinner | b261475 | 2014-08-25 23:20:52 +0200 | [diff] [blame] | 717 |             if self._loop.get_debug(): | 
 | 718 |                 logger.debug("%r: %s", self, message, exc_info=True) | 
 | 719 |         else: | 
| Yury Selivanov | 569efa2 | 2014-02-18 18:02:19 -0500 | [diff] [blame] | 720 |             self._loop.call_exception_handler({ | 
| Victor Stinner | 0ee29c2 | 2014-02-19 01:40:41 +0100 | [diff] [blame] | 721 |                 'message': message, | 
| Yury Selivanov | 569efa2 | 2014-02-18 18:02:19 -0500 | [diff] [blame] | 722 |                 'exception': exc, | 
 | 723 |                 'transport': self, | 
 | 724 |                 'protocol': self._protocol, | 
 | 725 |             }) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 726 |         self._close(exc) | 
 | 727 |  | 
 | 728 |     def _close(self, exc=None): | 
 | 729 |         self._closing = True | 
 | 730 |         if self._buffer: | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 731 |             self._loop._remove_writer(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 732 |         self._buffer.clear() | 
| Yury Selivanov | 5b8d4f9 | 2016-10-05 17:48:59 -0400 | [diff] [blame] | 733 |         self._loop._remove_reader(self._fileno) | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 734 |         self._loop.call_soon(self._call_connection_lost, exc) | 
 | 735 |  | 
 | 736 |     def _call_connection_lost(self, exc): | 
 | 737 |         try: | 
 | 738 |             self._protocol.connection_lost(exc) | 
 | 739 |         finally: | 
 | 740 |             self._pipe.close() | 
 | 741 |             self._pipe = None | 
 | 742 |             self._protocol = None | 
 | 743 |             self._loop = None | 
 | 744 |  | 
 | 745 |  | 
| Guido van Rossum | 5969128 | 2013-10-30 14:52:03 -0700 | [diff] [blame] | 746 | class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport): | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 747 |  | 
| Guido van Rossum | 5969128 | 2013-10-30 14:52:03 -0700 | [diff] [blame] | 748 |     def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs): | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 749 |         stdin_w = None | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 750 |         if stdin == subprocess.PIPE: | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 751 |             # Use a socket pair for stdin, since not all platforms | 
 | 752 |             # support selecting read events on the write end of a | 
 | 753 |             # socket (which we use in order to detect closing of the | 
 | 754 |             # other end).  Notably this is needed on AIX, and works | 
 | 755 |             # just fine on other platforms. | 
| Victor Stinner | a10dc3e | 2017-11-28 11:15:26 +0100 | [diff] [blame] | 756 |             stdin, stdin_w = socket.socketpair() | 
| Guido van Rossum | 27b7c7e | 2013-10-17 13:40:50 -0700 | [diff] [blame] | 757 |         self._proc = subprocess.Popen( | 
 | 758 |             args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr, | 
 | 759 |             universal_newlines=False, bufsize=bufsize, **kwargs) | 
| Guido van Rossum | 934f6ea | 2013-10-21 20:37:14 -0700 | [diff] [blame] | 760 |         if stdin_w is not None: | 
 | 761 |             stdin.close() | 
| Victor Stinner | 2dba23a | 2014-07-03 00:59:00 +0200 | [diff] [blame] | 762 |             self._proc.stdin = open(stdin_w.detach(), 'wb', buffering=bufsize) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 763 |  | 
 | 764 |  | 
 | 765 | class AbstractChildWatcher: | 
 | 766 |     """Abstract base class for monitoring child processes. | 
 | 767 |  | 
 | 768 |     Objects derived from this class monitor a collection of subprocesses and | 
 | 769 |     report their termination or interruption by a signal. | 
 | 770 |  | 
 | 771 |     New callbacks are registered with .add_child_handler(). Starting a new | 
 | 772 |     process must be done within a 'with' block to allow the watcher to suspend | 
 | 773 |     its activity until the new process if fully registered (this is needed to | 
 | 774 |     prevent a race condition in some implementations). | 
 | 775 |  | 
 | 776 |     Example: | 
 | 777 |         with watcher: | 
 | 778 |             proc = subprocess.Popen("sleep 1") | 
 | 779 |             watcher.add_child_handler(proc.pid, callback) | 
 | 780 |  | 
 | 781 |     Notes: | 
 | 782 |         Implementations of this class must be thread-safe. | 
 | 783 |  | 
 | 784 |         Since child watcher objects may catch the SIGCHLD signal and call | 
 | 785 |         waitpid(-1), there should be only one active object per process. | 
 | 786 |     """ | 
 | 787 |  | 
 | 788 |     def add_child_handler(self, pid, callback, *args): | 
 | 789 |         """Register a new child handler. | 
 | 790 |  | 
 | 791 |         Arrange for callback(pid, returncode, *args) to be called when | 
 | 792 |         process 'pid' terminates. Specifying another callback for the same | 
 | 793 |         process replaces the previous handler. | 
 | 794 |  | 
| Victor Stinner | acdb782 | 2014-07-14 18:33:40 +0200 | [diff] [blame] | 795 |         Note: callback() must be thread-safe. | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 796 |         """ | 
 | 797 |         raise NotImplementedError() | 
 | 798 |  | 
 | 799 |     def remove_child_handler(self, pid): | 
 | 800 |         """Removes the handler for process 'pid'. | 
 | 801 |  | 
 | 802 |         The function returns True if the handler was successfully removed, | 
 | 803 |         False if there was nothing to remove.""" | 
 | 804 |  | 
 | 805 |         raise NotImplementedError() | 
 | 806 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 807 |     def attach_loop(self, loop): | 
 | 808 |         """Attach the watcher to an event loop. | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 809 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 810 |         If the watcher was previously attached to an event loop, then it is | 
 | 811 |         first detached before attaching to the new loop. | 
 | 812 |  | 
 | 813 |         Note: loop may be None. | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 814 |         """ | 
 | 815 |         raise NotImplementedError() | 
 | 816 |  | 
 | 817 |     def close(self): | 
 | 818 |         """Close the watcher. | 
 | 819 |  | 
 | 820 |         This must be called to make sure that any underlying resource is freed. | 
 | 821 |         """ | 
 | 822 |         raise NotImplementedError() | 
 | 823 |  | 
 | 824 |     def __enter__(self): | 
 | 825 |         """Enter the watcher's context and allow starting new processes | 
 | 826 |  | 
 | 827 |         This function must return self""" | 
 | 828 |         raise NotImplementedError() | 
 | 829 |  | 
 | 830 |     def __exit__(self, a, b, c): | 
 | 831 |         """Exit the watcher's context""" | 
 | 832 |         raise NotImplementedError() | 
 | 833 |  | 
 | 834 |  | 
 | 835 | class BaseChildWatcher(AbstractChildWatcher): | 
 | 836 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 837 |     def __init__(self): | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 838 |         self._loop = None | 
| Yury Selivanov | 9eb6c67 | 2016-10-05 16:57:12 -0400 | [diff] [blame] | 839 |         self._callbacks = {} | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 840 |  | 
 | 841 |     def close(self): | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 842 |         self.attach_loop(None) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 843 |  | 
 | 844 |     def _do_waitpid(self, expected_pid): | 
 | 845 |         raise NotImplementedError() | 
 | 846 |  | 
 | 847 |     def _do_waitpid_all(self): | 
 | 848 |         raise NotImplementedError() | 
 | 849 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 850 |     def attach_loop(self, loop): | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 851 |         assert loop is None or isinstance(loop, events.AbstractEventLoop) | 
 | 852 |  | 
| Yury Selivanov | 9eb6c67 | 2016-10-05 16:57:12 -0400 | [diff] [blame] | 853 |         if self._loop is not None and loop is None and self._callbacks: | 
 | 854 |             warnings.warn( | 
 | 855 |                 'A loop is being detached ' | 
 | 856 |                 'from a child watcher with pending handlers', | 
 | 857 |                 RuntimeWarning) | 
 | 858 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 859 |         if self._loop is not None: | 
 | 860 |             self._loop.remove_signal_handler(signal.SIGCHLD) | 
 | 861 |  | 
 | 862 |         self._loop = loop | 
 | 863 |         if loop is not None: | 
 | 864 |             loop.add_signal_handler(signal.SIGCHLD, self._sig_chld) | 
 | 865 |  | 
 | 866 |             # Prevent a race condition in case a child terminated | 
 | 867 |             # during the switch. | 
 | 868 |             self._do_waitpid_all() | 
 | 869 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 870 |     def _sig_chld(self): | 
 | 871 |         try: | 
 | 872 |             self._do_waitpid_all() | 
| Yury Selivanov | 569efa2 | 2014-02-18 18:02:19 -0500 | [diff] [blame] | 873 |         except Exception as exc: | 
 | 874 |             # self._loop should always be available here | 
 | 875 |             # as '_sig_chld' is added as a signal handler | 
 | 876 |             # in 'attach_loop' | 
 | 877 |             self._loop.call_exception_handler({ | 
 | 878 |                 'message': 'Unknown exception in SIGCHLD handler', | 
 | 879 |                 'exception': exc, | 
 | 880 |             }) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 881 |  | 
 | 882 |     def _compute_returncode(self, status): | 
 | 883 |         if os.WIFSIGNALED(status): | 
 | 884 |             # The child process died because of a signal. | 
 | 885 |             return -os.WTERMSIG(status) | 
 | 886 |         elif os.WIFEXITED(status): | 
 | 887 |             # The child process exited (e.g sys.exit()). | 
 | 888 |             return os.WEXITSTATUS(status) | 
 | 889 |         else: | 
 | 890 |             # The child exited, but we don't understand its status. | 
 | 891 |             # This shouldn't happen, but if it does, let's just | 
 | 892 |             # return that status; perhaps that helps debug it. | 
 | 893 |             return status | 
 | 894 |  | 
 | 895 |  | 
 | 896 | class SafeChildWatcher(BaseChildWatcher): | 
 | 897 |     """'Safe' child watcher implementation. | 
 | 898 |  | 
 | 899 |     This implementation avoids disrupting other code spawning processes by | 
 | 900 |     polling explicitly each process in the SIGCHLD handler instead of calling | 
 | 901 |     os.waitpid(-1). | 
 | 902 |  | 
 | 903 |     This is a safe solution but it has a significant overhead when handling a | 
 | 904 |     big number of children (O(n) each time SIGCHLD is raised) | 
 | 905 |     """ | 
 | 906 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 907 |     def close(self): | 
 | 908 |         self._callbacks.clear() | 
 | 909 |         super().close() | 
 | 910 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 911 |     def __enter__(self): | 
 | 912 |         return self | 
 | 913 |  | 
 | 914 |     def __exit__(self, a, b, c): | 
 | 915 |         pass | 
 | 916 |  | 
 | 917 |     def add_child_handler(self, pid, callback, *args): | 
| Yury Selivanov | 9eb6c67 | 2016-10-05 16:57:12 -0400 | [diff] [blame] | 918 |         if self._loop is None: | 
 | 919 |             raise RuntimeError( | 
 | 920 |                 "Cannot add child handler, " | 
 | 921 |                 "the child watcher does not have a loop attached") | 
 | 922 |  | 
| Victor Stinner | 47cd10d | 2015-01-30 00:05:19 +0100 | [diff] [blame] | 923 |         self._callbacks[pid] = (callback, args) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 924 |  | 
 | 925 |         # Prevent a race condition in case the child is already terminated. | 
 | 926 |         self._do_waitpid(pid) | 
 | 927 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 928 |     def remove_child_handler(self, pid): | 
 | 929 |         try: | 
 | 930 |             del self._callbacks[pid] | 
 | 931 |             return True | 
 | 932 |         except KeyError: | 
 | 933 |             return False | 
 | 934 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 935 |     def _do_waitpid_all(self): | 
 | 936 |  | 
 | 937 |         for pid in list(self._callbacks): | 
 | 938 |             self._do_waitpid(pid) | 
 | 939 |  | 
 | 940 |     def _do_waitpid(self, expected_pid): | 
 | 941 |         assert expected_pid > 0 | 
 | 942 |  | 
 | 943 |         try: | 
 | 944 |             pid, status = os.waitpid(expected_pid, os.WNOHANG) | 
 | 945 |         except ChildProcessError: | 
 | 946 |             # The child process is already reaped | 
 | 947 |             # (may happen if waitpid() is called elsewhere). | 
 | 948 |             pid = expected_pid | 
 | 949 |             returncode = 255 | 
 | 950 |             logger.warning( | 
 | 951 |                 "Unknown child process pid %d, will report returncode 255", | 
 | 952 |                 pid) | 
 | 953 |         else: | 
 | 954 |             if pid == 0: | 
 | 955 |                 # The child process is still alive. | 
 | 956 |                 return | 
 | 957 |  | 
 | 958 |             returncode = self._compute_returncode(status) | 
| Victor Stinner | acdb782 | 2014-07-14 18:33:40 +0200 | [diff] [blame] | 959 |             if self._loop.get_debug(): | 
 | 960 |                 logger.debug('process %s exited with returncode %s', | 
 | 961 |                              expected_pid, returncode) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 962 |  | 
 | 963 |         try: | 
 | 964 |             callback, args = self._callbacks.pop(pid) | 
 | 965 |         except KeyError:  # pragma: no cover | 
 | 966 |             # May happen if .remove_child_handler() is called | 
 | 967 |             # after os.waitpid() returns. | 
| Victor Stinner | b261475 | 2014-08-25 23:20:52 +0200 | [diff] [blame] | 968 |             if self._loop.get_debug(): | 
 | 969 |                 logger.warning("Child watcher got an unexpected pid: %r", | 
 | 970 |                                pid, exc_info=True) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 971 |         else: | 
 | 972 |             callback(pid, returncode, *args) | 
 | 973 |  | 
 | 974 |  | 
 | 975 | class FastChildWatcher(BaseChildWatcher): | 
 | 976 |     """'Fast' child watcher implementation. | 
 | 977 |  | 
 | 978 |     This implementation reaps every terminated processes by calling | 
 | 979 |     os.waitpid(-1) directly, possibly breaking other code spawning processes | 
 | 980 |     and waiting for their termination. | 
 | 981 |  | 
 | 982 |     There is no noticeable overhead when handling a big number of children | 
 | 983 |     (O(1) each time a child terminates). | 
 | 984 |     """ | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 985 |     def __init__(self): | 
 | 986 |         super().__init__() | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 987 |         self._lock = threading.Lock() | 
 | 988 |         self._zombies = {} | 
 | 989 |         self._forks = 0 | 
 | 990 |  | 
 | 991 |     def close(self): | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 992 |         self._callbacks.clear() | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 993 |         self._zombies.clear() | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 994 |         super().close() | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 995 |  | 
 | 996 |     def __enter__(self): | 
 | 997 |         with self._lock: | 
 | 998 |             self._forks += 1 | 
 | 999 |  | 
 | 1000 |             return self | 
 | 1001 |  | 
 | 1002 |     def __exit__(self, a, b, c): | 
 | 1003 |         with self._lock: | 
 | 1004 |             self._forks -= 1 | 
 | 1005 |  | 
 | 1006 |             if self._forks or not self._zombies: | 
 | 1007 |                 return | 
 | 1008 |  | 
 | 1009 |             collateral_victims = str(self._zombies) | 
 | 1010 |             self._zombies.clear() | 
 | 1011 |  | 
 | 1012 |         logger.warning( | 
 | 1013 |             "Caught subprocesses termination from unknown pids: %s", | 
 | 1014 |             collateral_victims) | 
 | 1015 |  | 
 | 1016 |     def add_child_handler(self, pid, callback, *args): | 
 | 1017 |         assert self._forks, "Must use the context manager" | 
| Yury Selivanov | 9eb6c67 | 2016-10-05 16:57:12 -0400 | [diff] [blame] | 1018 |  | 
 | 1019 |         if self._loop is None: | 
 | 1020 |             raise RuntimeError( | 
 | 1021 |                 "Cannot add child handler, " | 
 | 1022 |                 "the child watcher does not have a loop attached") | 
 | 1023 |  | 
| Guido van Rossum | ab27a9f | 2014-01-25 16:32:17 -0800 | [diff] [blame] | 1024 |         with self._lock: | 
 | 1025 |             try: | 
 | 1026 |                 returncode = self._zombies.pop(pid) | 
 | 1027 |             except KeyError: | 
 | 1028 |                 # The child is running. | 
 | 1029 |                 self._callbacks[pid] = callback, args | 
 | 1030 |                 return | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1031 |  | 
| Guido van Rossum | ab27a9f | 2014-01-25 16:32:17 -0800 | [diff] [blame] | 1032 |         # The child is dead already. We can fire the callback. | 
 | 1033 |         callback(pid, returncode, *args) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1034 |  | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 1035 |     def remove_child_handler(self, pid): | 
 | 1036 |         try: | 
 | 1037 |             del self._callbacks[pid] | 
 | 1038 |             return True | 
 | 1039 |         except KeyError: | 
 | 1040 |             return False | 
 | 1041 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1042 |     def _do_waitpid_all(self): | 
 | 1043 |         # Because of signal coalescing, we must keep calling waitpid() as | 
 | 1044 |         # long as we're able to reap a child. | 
 | 1045 |         while True: | 
 | 1046 |             try: | 
 | 1047 |                 pid, status = os.waitpid(-1, os.WNOHANG) | 
 | 1048 |             except ChildProcessError: | 
 | 1049 |                 # No more child processes exist. | 
 | 1050 |                 return | 
 | 1051 |             else: | 
 | 1052 |                 if pid == 0: | 
 | 1053 |                     # A child process is still alive. | 
 | 1054 |                     return | 
 | 1055 |  | 
 | 1056 |                 returncode = self._compute_returncode(status) | 
 | 1057 |  | 
| Guido van Rossum | ab27a9f | 2014-01-25 16:32:17 -0800 | [diff] [blame] | 1058 |             with self._lock: | 
 | 1059 |                 try: | 
 | 1060 |                     callback, args = self._callbacks.pop(pid) | 
 | 1061 |                 except KeyError: | 
 | 1062 |                     # unknown child | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1063 |                     if self._forks: | 
 | 1064 |                         # It may not be registered yet. | 
 | 1065 |                         self._zombies[pid] = returncode | 
| Victor Stinner | acdb782 | 2014-07-14 18:33:40 +0200 | [diff] [blame] | 1066 |                         if self._loop.get_debug(): | 
 | 1067 |                             logger.debug('unknown process %s exited ' | 
 | 1068 |                                          'with returncode %s', | 
 | 1069 |                                          pid, returncode) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1070 |                         continue | 
| Guido van Rossum | ab27a9f | 2014-01-25 16:32:17 -0800 | [diff] [blame] | 1071 |                     callback = None | 
| Victor Stinner | acdb782 | 2014-07-14 18:33:40 +0200 | [diff] [blame] | 1072 |                 else: | 
 | 1073 |                     if self._loop.get_debug(): | 
 | 1074 |                         logger.debug('process %s exited with returncode %s', | 
 | 1075 |                                      pid, returncode) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1076 |  | 
| Guido van Rossum | ab27a9f | 2014-01-25 16:32:17 -0800 | [diff] [blame] | 1077 |             if callback is None: | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1078 |                 logger.warning( | 
 | 1079 |                     "Caught subprocess termination from unknown pid: " | 
 | 1080 |                     "%d -> %d", pid, returncode) | 
 | 1081 |             else: | 
 | 1082 |                 callback(pid, returncode, *args) | 
 | 1083 |  | 
 | 1084 |  | 
 | 1085 | class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy): | 
| Victor Stinner | 70db9e4 | 2015-01-09 21:32:05 +0100 | [diff] [blame] | 1086 |     """UNIX event loop policy with a watcher for child processes.""" | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1087 |     _loop_factory = _UnixSelectorEventLoop | 
 | 1088 |  | 
 | 1089 |     def __init__(self): | 
 | 1090 |         super().__init__() | 
 | 1091 |         self._watcher = None | 
 | 1092 |  | 
 | 1093 |     def _init_watcher(self): | 
 | 1094 |         with events._lock: | 
 | 1095 |             if self._watcher is None:  # pragma: no branch | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 1096 |                 self._watcher = SafeChildWatcher() | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1097 |                 if isinstance(threading.current_thread(), | 
 | 1098 |                               threading._MainThread): | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 1099 |                     self._watcher.attach_loop(self._local._loop) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1100 |  | 
 | 1101 |     def set_event_loop(self, loop): | 
 | 1102 |         """Set the event loop. | 
 | 1103 |  | 
 | 1104 |         As a side effect, if a child watcher was set before, then calling | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 1105 |         .set_event_loop() from the main thread will call .attach_loop(loop) on | 
 | 1106 |         the child watcher. | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1107 |         """ | 
 | 1108 |  | 
 | 1109 |         super().set_event_loop(loop) | 
 | 1110 |  | 
| Andrew Svetlov | cc83920 | 2017-11-29 18:23:43 +0200 | [diff] [blame] | 1111 |         if (self._watcher is not None and | 
 | 1112 |                 isinstance(threading.current_thread(), threading._MainThread)): | 
| Guido van Rossum | 2bcae70 | 2013-11-13 15:50:08 -0800 | [diff] [blame] | 1113 |             self._watcher.attach_loop(loop) | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1114 |  | 
 | 1115 |     def get_child_watcher(self): | 
| Victor Stinner | f9e49dd | 2014-06-05 12:06:44 +0200 | [diff] [blame] | 1116 |         """Get the watcher for child processes. | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1117 |  | 
 | 1118 |         If not yet set, a SafeChildWatcher object is automatically created. | 
 | 1119 |         """ | 
 | 1120 |         if self._watcher is None: | 
 | 1121 |             self._init_watcher() | 
 | 1122 |  | 
 | 1123 |         return self._watcher | 
 | 1124 |  | 
 | 1125 |     def set_child_watcher(self, watcher): | 
| Victor Stinner | f9e49dd | 2014-06-05 12:06:44 +0200 | [diff] [blame] | 1126 |         """Set the watcher for child processes.""" | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1127 |  | 
 | 1128 |         assert watcher is None or isinstance(watcher, AbstractChildWatcher) | 
 | 1129 |  | 
 | 1130 |         if self._watcher is not None: | 
 | 1131 |             self._watcher.close() | 
 | 1132 |  | 
 | 1133 |         self._watcher = watcher | 
 | 1134 |  | 
| Yury Selivanov | 6370f34 | 2017-12-10 18:36:12 -0500 | [diff] [blame] | 1135 |  | 
| Guido van Rossum | 0eaa5ac | 2013-11-04 15:50:46 -0800 | [diff] [blame] | 1136 | SelectorEventLoop = _UnixSelectorEventLoop | 
 | 1137 | DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy |