Coverage for Lib/asyncio/windows_events.py: 0%
546 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:31 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:31 +0000
1"""Selector and proactor event loops for Windows."""
3import sys
5if sys.platform != 'win32': # pragma: no cover
6 raise ImportError('win32 only')
8import _overlapped
9import _winapi
10import errno
11from functools import partial
12import math
13import msvcrt
14import socket
15import struct
16import time
17import weakref
19from . import base_subprocess
20from . import futures
21from . import exceptions
22from . import proactor_events
23from . import selector_events
24from . import tasks
25from . import windows_utils
26from .log import logger
29__all__ = (
30 'SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor',
31 'EventLoop',
32)
35NULL = _winapi.NULL
36INFINITE = _winapi.INFINITE
37ERROR_CONNECTION_REFUSED = 1225
38ERROR_CONNECTION_ABORTED = 1236
40# Initial delay in seconds for connect_pipe() before retrying to connect
41CONNECT_PIPE_INIT_DELAY = 0.001
43# Maximum delay in seconds for connect_pipe() before retrying to connect
44CONNECT_PIPE_MAX_DELAY = 0.100
47class _OverlappedFuture(futures.Future):
48 """Subclass of Future which represents an overlapped operation.
50 Cancelling it will immediately cancel the overlapped operation.
51 """
53 def __init__(self, ov, *, loop=None):
54 super().__init__(loop=loop)
55 if self._source_traceback:
56 del self._source_traceback[-1]
57 self._ov = ov
59 def _repr_info(self):
60 info = super()._repr_info()
61 if self._ov is not None:
62 state = 'pending' if self._ov.pending else 'completed'
63 info.insert(1, f'overlapped=<{state}, {self._ov.address:#x}>')
64 return info
66 def _cancel_overlapped(self):
67 if self._ov is None:
68 return
69 try:
70 self._ov.cancel()
71 except OSError as exc:
72 context = {
73 'message': 'Cancelling an overlapped future failed',
74 'exception': exc,
75 'future': self,
76 }
77 if self._source_traceback:
78 context['source_traceback'] = self._source_traceback
79 self._loop.call_exception_handler(context)
80 self._ov = None
82 def cancel(self, msg=None):
83 self._cancel_overlapped()
84 return super().cancel(msg=msg)
86 def set_exception(self, exception):
87 super().set_exception(exception)
88 self._cancel_overlapped()
90 def set_result(self, result):
91 super().set_result(result)
92 self._ov = None
95class _BaseWaitHandleFuture(futures.Future):
96 """Subclass of Future which represents a wait handle."""
98 def __init__(self, ov, handle, wait_handle, *, loop=None):
99 super().__init__(loop=loop)
100 if self._source_traceback:
101 del self._source_traceback[-1]
102 # Keep a reference to the Overlapped object to keep it alive until the
103 # wait is unregistered
104 self._ov = ov
105 self._handle = handle
106 self._wait_handle = wait_handle
108 # Should we call UnregisterWaitEx() if the wait completes
109 # or is cancelled?
110 self._registered = True
112 def _poll(self):
113 # non-blocking wait: use a timeout of 0 millisecond
114 return (_winapi.WaitForSingleObject(self._handle, 0) ==
115 _winapi.WAIT_OBJECT_0)
117 def _repr_info(self):
118 info = super()._repr_info()
119 info.append(f'handle={self._handle:#x}')
120 if self._handle is not None:
121 state = 'signaled' if self._poll() else 'waiting'
122 info.append(state)
123 if self._wait_handle is not None:
124 info.append(f'wait_handle={self._wait_handle:#x}')
125 return info
127 def _unregister_wait_cb(self, fut):
128 # The wait was unregistered: it's not safe to destroy the Overlapped
129 # object
130 self._ov = None
132 def _unregister_wait(self):
133 if not self._registered:
134 return
135 self._registered = False
137 wait_handle = self._wait_handle
138 self._wait_handle = None
139 try:
140 _overlapped.UnregisterWait(wait_handle)
141 except OSError as exc:
142 if exc.winerror != _overlapped.ERROR_IO_PENDING:
143 context = {
144 'message': 'Failed to unregister the wait handle',
145 'exception': exc,
146 'future': self,
147 }
148 if self._source_traceback:
149 context['source_traceback'] = self._source_traceback
150 self._loop.call_exception_handler(context)
151 return
152 # ERROR_IO_PENDING means that the unregister is pending
154 self._unregister_wait_cb(None)
156 def cancel(self, msg=None):
157 self._unregister_wait()
158 return super().cancel(msg=msg)
160 def set_exception(self, exception):
161 self._unregister_wait()
162 super().set_exception(exception)
164 def set_result(self, result):
165 self._unregister_wait()
166 super().set_result(result)
169class _WaitCancelFuture(_BaseWaitHandleFuture):
170 """Subclass of Future which represents a wait for the cancellation of a
171 _WaitHandleFuture using an event.
172 """
174 def __init__(self, ov, event, wait_handle, *, loop=None):
175 super().__init__(ov, event, wait_handle, loop=loop)
177 self._done_callback = None
179 def cancel(self):
180 raise RuntimeError("_WaitCancelFuture must not be cancelled")
182 def set_result(self, result):
183 super().set_result(result)
184 if self._done_callback is not None:
185 self._done_callback(self)
187 def set_exception(self, exception):
188 super().set_exception(exception)
189 if self._done_callback is not None:
190 self._done_callback(self)
193class _WaitHandleFuture(_BaseWaitHandleFuture):
194 def __init__(self, ov, handle, wait_handle, proactor, *, loop=None):
195 super().__init__(ov, handle, wait_handle, loop=loop)
196 self._proactor = proactor
197 self._unregister_proactor = True
198 self._event = _overlapped.CreateEvent(None, True, False, None)
199 self._event_fut = None
201 def _unregister_wait_cb(self, fut):
202 if self._event is not None:
203 _winapi.CloseHandle(self._event)
204 self._event = None
205 self._event_fut = None
207 # If the wait was cancelled, the wait may never be signalled, so
208 # it's required to unregister it. Otherwise, IocpProactor.close() will
209 # wait forever for an event which will never come.
210 #
211 # If the IocpProactor already received the event, it's safe to call
212 # _unregister() because we kept a reference to the Overlapped object
213 # which is used as a unique key.
214 self._proactor._unregister(self._ov)
215 self._proactor = None
217 super()._unregister_wait_cb(fut)
219 def _unregister_wait(self):
220 if not self._registered:
221 return
222 self._registered = False
224 wait_handle = self._wait_handle
225 self._wait_handle = None
226 try:
227 _overlapped.UnregisterWaitEx(wait_handle, self._event)
228 except OSError as exc:
229 if exc.winerror != _overlapped.ERROR_IO_PENDING:
230 context = {
231 'message': 'Failed to unregister the wait handle',
232 'exception': exc,
233 'future': self,
234 }
235 if self._source_traceback:
236 context['source_traceback'] = self._source_traceback
237 self._loop.call_exception_handler(context)
238 return
239 # ERROR_IO_PENDING is not an error, the wait was unregistered
241 self._event_fut = self._proactor._wait_cancel(self._event,
242 self._unregister_wait_cb)
245class PipeServer(object):
246 """Class representing a pipe server.
248 This is much like a bound, listening socket.
249 """
250 def __init__(self, address):
251 self._address = address
252 self._free_instances = weakref.WeakSet()
253 # initialize the pipe attribute before calling _server_pipe_handle()
254 # because this function can raise an exception and the destructor calls
255 # the close() method
256 self._pipe = None
257 self._accept_pipe_future = None
258 self._pipe = self._server_pipe_handle(True)
260 def _get_unconnected_pipe(self):
261 # Create new instance and return previous one. This ensures
262 # that (until the server is closed) there is always at least
263 # one pipe handle for address. Therefore if a client attempt
264 # to connect it will not fail with FileNotFoundError.
265 tmp, self._pipe = self._pipe, self._server_pipe_handle(False)
266 return tmp
268 def _server_pipe_handle(self, first):
269 # Return a wrapper for a new pipe handle.
270 if self.closed():
271 return None
272 flags = _winapi.PIPE_ACCESS_DUPLEX | _winapi.FILE_FLAG_OVERLAPPED
273 if first:
274 flags |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
275 h = _winapi.CreateNamedPipe(
276 self._address, flags,
277 _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
278 _winapi.PIPE_WAIT,
279 _winapi.PIPE_UNLIMITED_INSTANCES,
280 windows_utils.BUFSIZE, windows_utils.BUFSIZE,
281 _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
282 pipe = windows_utils.PipeHandle(h)
283 self._free_instances.add(pipe)
284 return pipe
286 def closed(self):
287 return (self._address is None)
289 def close(self):
290 if self._accept_pipe_future is not None:
291 self._accept_pipe_future.cancel()
292 self._accept_pipe_future = None
293 # Close all instances which have not been connected to by a client.
294 if self._address is not None:
295 for pipe in self._free_instances:
296 pipe.close()
297 self._pipe = None
298 self._address = None
299 self._free_instances.clear()
301 __del__ = close
304class _WindowsSelectorEventLoop(selector_events.BaseSelectorEventLoop):
305 """Windows version of selector event loop."""
308class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
309 """Windows version of proactor event loop using IOCP."""
311 def __init__(self, proactor=None):
312 if proactor is None:
313 proactor = IocpProactor()
314 super().__init__(proactor)
316 def _run_forever_setup(self):
317 assert self._self_reading_future is None
318 self.call_soon(self._loop_self_reading)
319 super()._run_forever_setup()
321 def _run_forever_cleanup(self):
322 super()._run_forever_cleanup()
323 if self._self_reading_future is not None:
324 ov = self._self_reading_future._ov
325 self._self_reading_future.cancel()
326 # self_reading_future always uses IOCP, so even though it's
327 # been cancelled, we need to make sure that the IOCP message
328 # is received so that the kernel is not holding on to the
329 # memory, possibly causing memory corruption later. Only
330 # unregister it if IO is complete in all respects. Otherwise
331 # we need another _poll() later to complete the IO.
332 if ov is not None and not ov.pending:
333 self._proactor._unregister(ov)
334 self._self_reading_future = None
336 async def create_pipe_connection(self, protocol_factory, address):
337 f = self._proactor.connect_pipe(address)
338 pipe = await f
339 protocol = protocol_factory()
340 trans = self._make_duplex_pipe_transport(pipe, protocol,
341 extra={'addr': address})
342 return trans, protocol
344 async def start_serving_pipe(self, protocol_factory, address):
345 server = PipeServer(address)
347 def loop_accept_pipe(f=None):
348 pipe = None
349 try:
350 if f:
351 pipe = f.result()
352 server._free_instances.discard(pipe)
354 if server.closed():
355 # A client connected before the server was closed:
356 # drop the client (close the pipe) and exit
357 pipe.close()
358 return
360 protocol = protocol_factory()
361 self._make_duplex_pipe_transport(
362 pipe, protocol, extra={'addr': address})
364 pipe = server._get_unconnected_pipe()
365 if pipe is None:
366 return
368 f = self._proactor.accept_pipe(pipe)
369 except BrokenPipeError:
370 if pipe and pipe.fileno() != -1:
371 pipe.close()
372 self.call_soon(loop_accept_pipe)
373 except OSError as exc:
374 if pipe and pipe.fileno() != -1:
375 self.call_exception_handler({
376 'message': 'Pipe accept failed',
377 'exception': exc,
378 'pipe': pipe,
379 })
380 pipe.close()
381 elif self._debug:
382 logger.warning("Accept pipe failed on pipe %r",
383 pipe, exc_info=True)
384 self.call_soon(loop_accept_pipe)
385 except exceptions.CancelledError:
386 if pipe:
387 pipe.close()
388 else:
389 server._accept_pipe_future = f
390 f.add_done_callback(loop_accept_pipe)
392 self.call_soon(loop_accept_pipe)
393 return [server]
395 async def _make_subprocess_transport(self, protocol, args, shell,
396 stdin, stdout, stderr, bufsize,
397 extra=None, **kwargs):
398 waiter = self.create_future()
399 transp = _WindowsSubprocessTransport(self, protocol, args, shell,
400 stdin, stdout, stderr, bufsize,
401 waiter=waiter, extra=extra,
402 **kwargs)
403 try:
404 await waiter
405 except (SystemExit, KeyboardInterrupt):
406 raise
407 except BaseException:
408 transp.close()
409 await tasks.shield(transp._wait())
410 raise
412 return transp
415class IocpProactor:
416 """Proactor implementation using IOCP."""
418 def __init__(self, concurrency=INFINITE):
419 self._loop = None
420 self._results = []
421 self._iocp = _overlapped.CreateIoCompletionPort(
422 _overlapped.INVALID_HANDLE_VALUE, NULL, 0, concurrency)
423 self._cache = {}
424 self._registered = weakref.WeakSet()
425 self._unregistered = []
426 self._stopped_serving = weakref.WeakSet()
428 def _check_closed(self):
429 if self._iocp is None:
430 raise RuntimeError('IocpProactor is closed')
432 def __repr__(self):
433 info = ['overlapped#=%s' % len(self._cache),
434 'result#=%s' % len(self._results)]
435 if self._iocp is None:
436 info.append('closed')
437 return '<%s %s>' % (self.__class__.__name__, " ".join(info))
439 def set_loop(self, loop):
440 self._loop = loop
442 def select(self, timeout=None):
443 if not self._results:
444 self._poll(timeout)
445 tmp = self._results
446 self._results = []
447 try:
448 return tmp
449 finally:
450 # Needed to break cycles when an exception occurs.
451 tmp = None
453 def _result(self, value):
454 fut = self._loop.create_future()
455 fut.set_result(value)
456 return fut
458 @staticmethod
459 def finish_socket_func(trans, key, ov):
460 try:
461 return ov.getresult()
462 except OSError as exc:
463 if exc.winerror in (_overlapped.ERROR_NETNAME_DELETED,
464 _overlapped.ERROR_OPERATION_ABORTED):
465 raise ConnectionResetError(*exc.args)
466 else:
467 raise
469 @classmethod
470 def _finish_recvfrom(cls, trans, key, ov, *, empty_result):
471 try:
472 return cls.finish_socket_func(trans, key, ov)
473 except OSError as exc:
474 # WSARecvFrom will report ERROR_PORT_UNREACHABLE when the same
475 # socket is used to send to an address that is not listening.
476 if exc.winerror == _overlapped.ERROR_PORT_UNREACHABLE:
477 return empty_result, None
478 else:
479 raise
481 def recv(self, conn, nbytes, flags=0):
482 self._register_with_iocp(conn)
483 ov = _overlapped.Overlapped(NULL)
484 try:
485 if isinstance(conn, socket.socket):
486 ov.WSARecv(conn.fileno(), nbytes, flags)
487 else:
488 ov.ReadFile(conn.fileno(), nbytes)
489 except BrokenPipeError:
490 return self._result(b'')
492 return self._register(ov, conn, self.finish_socket_func)
494 def recv_into(self, conn, buf, flags=0):
495 self._register_with_iocp(conn)
496 ov = _overlapped.Overlapped(NULL)
497 try:
498 if isinstance(conn, socket.socket):
499 ov.WSARecvInto(conn.fileno(), buf, flags)
500 else:
501 ov.ReadFileInto(conn.fileno(), buf)
502 except BrokenPipeError:
503 return self._result(0)
505 return self._register(ov, conn, self.finish_socket_func)
507 def recvfrom(self, conn, nbytes, flags=0):
508 self._register_with_iocp(conn)
509 ov = _overlapped.Overlapped(NULL)
510 try:
511 ov.WSARecvFrom(conn.fileno(), nbytes, flags)
512 except BrokenPipeError:
513 return self._result((b'', None))
515 return self._register(ov, conn, partial(self._finish_recvfrom,
516 empty_result=b''))
518 def recvfrom_into(self, conn, buf, flags=0):
519 self._register_with_iocp(conn)
520 ov = _overlapped.Overlapped(NULL)
521 try:
522 ov.WSARecvFromInto(conn.fileno(), buf, flags)
523 except BrokenPipeError:
524 return self._result((0, None))
526 return self._register(ov, conn, partial(self._finish_recvfrom,
527 empty_result=0))
529 def sendto(self, conn, buf, flags=0, addr=None):
530 self._register_with_iocp(conn)
531 ov = _overlapped.Overlapped(NULL)
533 ov.WSASendTo(conn.fileno(), buf, flags, addr)
535 return self._register(ov, conn, self.finish_socket_func)
537 def send(self, conn, buf, flags=0):
538 self._register_with_iocp(conn)
539 ov = _overlapped.Overlapped(NULL)
540 if isinstance(conn, socket.socket):
541 ov.WSASend(conn.fileno(), buf, flags)
542 else:
543 ov.WriteFile(conn.fileno(), buf)
545 return self._register(ov, conn, self.finish_socket_func)
547 def accept(self, listener):
548 self._register_with_iocp(listener)
549 conn = self._get_accept_socket(listener.family)
550 ov = _overlapped.Overlapped(NULL)
551 ov.AcceptEx(listener.fileno(), conn.fileno())
553 def finish_accept(trans, key, ov):
554 ov.getresult()
555 # Use SO_UPDATE_ACCEPT_CONTEXT so getsockname() etc work.
556 buf = struct.pack('@P', listener.fileno())
557 conn.setsockopt(socket.SOL_SOCKET,
558 _overlapped.SO_UPDATE_ACCEPT_CONTEXT, buf)
559 conn.settimeout(listener.gettimeout())
560 return conn, conn.getpeername()
562 async def accept_coro(future, conn):
563 # Coroutine closing the accept socket if the future is cancelled
564 try:
565 await future
566 except exceptions.CancelledError:
567 conn.close()
568 raise
570 future = self._register(ov, listener, finish_accept)
571 coro = accept_coro(future, conn)
572 tasks.ensure_future(coro, loop=self._loop)
573 return future
575 def connect(self, conn, address):
576 if conn.type == socket.SOCK_DGRAM:
577 # WSAConnect will complete immediately for UDP sockets so we don't
578 # need to register any IOCP operation
579 _overlapped.WSAConnect(conn.fileno(), address)
580 fut = self._loop.create_future()
581 fut.set_result(None)
582 return fut
584 self._register_with_iocp(conn)
585 # The socket needs to be locally bound before we call ConnectEx().
586 try:
587 _overlapped.BindLocal(conn.fileno(), conn.family)
588 except OSError as e:
589 if e.winerror != errno.WSAEINVAL:
590 raise
591 # Probably already locally bound; check using getsockname().
592 if conn.getsockname()[1] == 0:
593 raise
594 ov = _overlapped.Overlapped(NULL)
595 ov.ConnectEx(conn.fileno(), address)
597 def finish_connect(trans, key, ov):
598 ov.getresult()
599 # Use SO_UPDATE_CONNECT_CONTEXT so getsockname() etc work.
600 conn.setsockopt(socket.SOL_SOCKET,
601 _overlapped.SO_UPDATE_CONNECT_CONTEXT, 0)
602 return conn
604 return self._register(ov, conn, finish_connect)
606 def sendfile(self, sock, file, offset, count):
607 self._register_with_iocp(sock)
608 ov = _overlapped.Overlapped(NULL)
609 offset_low = offset & 0xffff_ffff
610 offset_high = (offset >> 32) & 0xffff_ffff
611 # TransmitFile ignores OVERLAPPED.Offset for handles not opened with
612 # FILE_FLAG_OVERLAPPED, so seek the CRT file pointer to match.
613 file.seek(offset)
614 ov.TransmitFile(sock.fileno(),
615 msvcrt.get_osfhandle(file.fileno()),
616 offset_low, offset_high,
617 count, 0, 0)
619 return self._register(ov, sock, self.finish_socket_func)
621 def accept_pipe(self, pipe):
622 self._register_with_iocp(pipe)
623 ov = _overlapped.Overlapped(NULL)
624 connected = ov.ConnectNamedPipe(pipe.fileno())
626 if connected:
627 # ConnectNamePipe() failed with ERROR_PIPE_CONNECTED which means
628 # that the pipe is connected. There is no need to wait for the
629 # completion of the connection.
630 return self._result(pipe)
632 def finish_accept_pipe(trans, key, ov):
633 ov.getresult()
634 return pipe
636 return self._register(ov, pipe, finish_accept_pipe)
638 async def connect_pipe(self, address):
639 delay = CONNECT_PIPE_INIT_DELAY
640 while True:
641 # Unfortunately there is no way to do an overlapped connect to
642 # a pipe. Call CreateFile() in a loop until it doesn't fail with
643 # ERROR_PIPE_BUSY.
644 try:
645 handle = _overlapped.ConnectPipe(address)
646 break
647 except OSError as exc:
648 if exc.winerror != _overlapped.ERROR_PIPE_BUSY:
649 raise
651 # ConnectPipe() failed with ERROR_PIPE_BUSY: retry later
652 delay = min(delay * 2, CONNECT_PIPE_MAX_DELAY)
653 await tasks.sleep(delay)
655 return windows_utils.PipeHandle(handle)
657 def wait_for_handle(self, handle, timeout=None):
658 """Wait for a handle.
660 Return a Future object. The result of the future is True if the wait
661 completed, or False if the wait did not complete (on timeout).
662 """
663 return self._wait_for_handle(handle, timeout, False)
665 def _wait_cancel(self, event, done_callback):
666 fut = self._wait_for_handle(event, None, True)
667 # add_done_callback() cannot be used because the wait may only complete
668 # in IocpProactor.close(), while the event loop is not running.
669 fut._done_callback = done_callback
670 return fut
672 def _wait_for_handle(self, handle, timeout, _is_cancel):
673 self._check_closed()
675 if timeout is None:
676 ms = _winapi.INFINITE
677 else:
678 # RegisterWaitForSingleObject() has a resolution of 1 millisecond,
679 # round away from zero to wait *at least* timeout seconds.
680 ms = math.ceil(timeout * 1e3)
682 # We only create ov so we can use ov.address as a key for the cache.
683 ov = _overlapped.Overlapped(NULL)
684 wait_handle = _overlapped.RegisterWaitWithQueue(
685 handle, self._iocp, ov.address, ms)
686 if _is_cancel:
687 f = _WaitCancelFuture(ov, handle, wait_handle, loop=self._loop)
688 else:
689 f = _WaitHandleFuture(ov, handle, wait_handle, self,
690 loop=self._loop)
691 if f._source_traceback:
692 del f._source_traceback[-1]
694 def finish_wait_for_handle(trans, key, ov):
695 # Note that this second wait means that we should only use
696 # this with handles types where a successful wait has no
697 # effect. So events or processes are all right, but locks
698 # or semaphores are not. Also note if the handle is
699 # signalled and then quickly reset, then we may return
700 # False even though we have not timed out.
701 return f._poll()
703 self._cache[ov.address] = (f, ov, 0, finish_wait_for_handle)
704 return f
706 def _register_with_iocp(self, obj):
707 # To get notifications of finished ops on this objects sent to the
708 # completion port, were must register the handle.
709 if obj not in self._registered:
710 self._registered.add(obj)
711 _overlapped.CreateIoCompletionPort(obj.fileno(), self._iocp, 0, 0)
712 # XXX We could also use SetFileCompletionNotificationModes()
713 # to avoid sending notifications to completion port of ops
714 # that succeed immediately.
716 def _register(self, ov, obj, callback):
717 self._check_closed()
719 # Return a future which will be set with the result of the
720 # operation when it completes. The future's value is actually
721 # the value returned by callback().
722 f = _OverlappedFuture(ov, loop=self._loop)
723 if f._source_traceback:
724 del f._source_traceback[-1]
725 if not ov.pending:
726 # The operation has completed, so no need to postpone the
727 # work. We cannot take this short cut if we need the
728 # NumberOfBytes, CompletionKey values returned by
729 # PostQueuedCompletionStatus().
730 try:
731 value = callback(None, None, ov)
732 except OSError as e:
733 f.set_exception(e)
734 else:
735 f.set_result(value)
736 # Even if GetOverlappedResult() was called, we have to wait for the
737 # notification of the completion in GetQueuedCompletionStatus().
738 # Register the overlapped operation to keep a reference to the
739 # OVERLAPPED object, otherwise the memory is freed and Windows may
740 # read uninitialized memory.
742 # Register the overlapped operation for later. Note that
743 # we only store obj to prevent it from being garbage
744 # collected too early.
745 self._cache[ov.address] = (f, ov, obj, callback)
746 return f
748 def _unregister(self, ov):
749 """Unregister an overlapped object.
751 Call this method when its future has been cancelled. The event can
752 already be signalled (pending in the proactor event queue). It is also
753 safe if the event is never signalled (because it was cancelled).
754 """
755 self._check_closed()
756 self._unregistered.append(ov)
758 def _get_accept_socket(self, family):
759 s = socket.socket(family)
760 s.settimeout(0)
761 return s
763 def _process_completion_status(self, status):
764 """Process a single status from the completion port.
766 A caller that waits on the completion port itself can pass each
767 status it receives here.
768 """
769 err, transferred, key, address = status
770 try:
771 f, ov, obj, callback = self._cache.pop(address)
772 except KeyError:
773 if self._loop.get_debug():
774 self._loop.call_exception_handler({
775 'message': ('GetQueuedCompletionStatus() returned an '
776 'unexpected event'),
777 'status': ('err=%s transferred=%s key=%#x address=%#x'
778 % (err, transferred, key, address)),
779 })
781 # key is either zero, or it is used to return a pipe
782 # handle which should be closed to avoid a leak.
783 if key not in (0, _overlapped.INVALID_HANDLE_VALUE):
784 _winapi.CloseHandle(key)
785 return
787 if obj in self._stopped_serving:
788 f.cancel()
789 # Don't call the callback if _register() already read the result or
790 # if the overlapped has been cancelled
791 elif not f.done():
792 try:
793 value = callback(transferred, key, ov)
794 except OSError as e:
795 f.set_exception(e)
796 self._results.append(f)
797 else:
798 f.set_result(value)
799 self._results.append(f)
800 finally:
801 f = None
803 def _poll(self, timeout=None):
804 if timeout is None:
805 ms = INFINITE
806 elif timeout < 0:
807 raise ValueError("negative timeout")
808 else:
809 # GetQueuedCompletionStatus() has a resolution of 1 millisecond,
810 # round away from zero to wait *at least* timeout seconds.
811 ms = math.ceil(timeout * 1e3)
812 if ms >= INFINITE:
813 raise ValueError("timeout too big")
815 while True:
816 status = _overlapped.GetQueuedCompletionStatus(self._iocp, ms)
817 if status is None:
818 break
819 ms = 0
821 # gh-154971: split out so custom event loops can call it directly
822 self._process_completion_status(status)
824 # Remove unregistered futures
825 for ov in self._unregistered:
826 self._cache.pop(ov.address, None)
827 self._unregistered.clear()
829 def _stop_serving(self, obj):
830 # obj is a socket or pipe handle. It will be closed in
831 # BaseProactorEventLoop._stop_serving() which will make any
832 # pending operations fail quickly.
833 self._stopped_serving.add(obj)
835 def close(self):
836 if self._iocp is None:
837 # already closed
838 return
840 # Cancel remaining registered operations.
841 for fut, ov, obj, callback in list(self._cache.values()):
842 if fut.cancelled():
843 # Nothing to do with cancelled futures
844 pass
845 elif isinstance(fut, _WaitCancelFuture):
846 # _WaitCancelFuture must not be cancelled
847 pass
848 else:
849 try:
850 fut.cancel()
851 except OSError as exc:
852 if self._loop is not None:
853 context = {
854 'message': 'Cancelling a future failed',
855 'exception': exc,
856 'future': fut,
857 }
858 if fut._source_traceback:
859 context['source_traceback'] = fut._source_traceback
860 self._loop.call_exception_handler(context)
862 # Wait until all cancelled overlapped complete: don't exit with running
863 # overlapped to prevent a crash. Display progress every second if the
864 # loop is still running.
865 msg_update = 1.0
866 start_time = time.monotonic()
867 next_msg = start_time + msg_update
868 while self._cache:
869 if next_msg <= time.monotonic():
870 logger.debug('%r is running after closing for %.1f seconds',
871 self, time.monotonic() - start_time)
872 next_msg = time.monotonic() + msg_update
874 # handle a few events, or timeout
875 self._poll(msg_update)
877 self._results = []
879 _winapi.CloseHandle(self._iocp)
880 self._iocp = None
882 def __del__(self):
883 self.close()
886class _WindowsSubprocessTransport(base_subprocess.BaseSubprocessTransport):
888 def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
889 self._proc = windows_utils.Popen(
890 args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
891 bufsize=bufsize, **kwargs)
893 def callback(f):
894 returncode = self._proc.poll()
895 self._process_exited(returncode)
897 f = self._loop._proactor.wait_for_handle(int(self._proc._handle))
898 f.add_done_callback(callback)
901SelectorEventLoop = _WindowsSelectorEventLoop
903EventLoop = ProactorEventLoop