[3.7] bpo-34323: Enhance IocpProactor.close() log (GH-11565)

* IocpProactor: prevent modification if closed (GH-11494)

* _wait_for_handle(), _register() and _unregister() methods of
  IocpProactor now raise an exception if closed
* Add "closed" to IocpProactor.__repr__()
* Simplify IocpProactor.close()

(cherry picked from commit 9b07681c09)

* bpo-34323: Enhance IocpProactor.close() log (GH-11555)

IocpProactor.close() now uses time to decide when to log: wait 1
second before the first log, then log every second. Log also the
number of seconds since close() was called.

(cherry picked from commit b1e45739d8)

* bpo-34323: Enhance IocpProactor.close() log again (GH-11563)

Add repr(self) to the log to display the number of pending overlapped
in the log.

(cherry picked from commit b91140fdb1)
This commit is contained in:
Victor Stinner 2019-01-15 13:05:28 +01:00 committed by GitHub
parent 97d6a56d9d
commit d5a6adf628
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 8 deletions

View File

@ -7,6 +7,7 @@ import math
import msvcrt import msvcrt
import socket import socket
import struct import struct
import time
import weakref import weakref
from . import events from . import events
@ -392,10 +393,16 @@ class IocpProactor:
self._unregistered = [] self._unregistered = []
self._stopped_serving = weakref.WeakSet() self._stopped_serving = weakref.WeakSet()
def _check_closed(self):
if self._iocp is None:
raise RuntimeError('IocpProactor is closed')
def __repr__(self): def __repr__(self):
return ('<%s overlapped#=%s result#=%s>' info = ['overlapped#=%s' % len(self._cache),
% (self.__class__.__name__, len(self._cache), 'result#=%s' % len(self._results)]
len(self._results))) if self._iocp is None:
info.append('closed')
return '<%s %s>' % (self.__class__.__name__, " ".join(info))
def set_loop(self, loop): def set_loop(self, loop):
self._loop = loop self._loop = loop
@ -602,6 +609,8 @@ class IocpProactor:
return fut return fut
def _wait_for_handle(self, handle, timeout, _is_cancel): def _wait_for_handle(self, handle, timeout, _is_cancel):
self._check_closed()
if timeout is None: if timeout is None:
ms = _winapi.INFINITE ms = _winapi.INFINITE
else: else:
@ -644,6 +653,8 @@ class IocpProactor:
# that succeed immediately. # that succeed immediately.
def _register(self, ov, obj, callback): def _register(self, ov, obj, callback):
self._check_closed()
# Return a future which will be set with the result of the # Return a future which will be set with the result of the
# operation when it completes. The future's value is actually # operation when it completes. The future's value is actually
# the value returned by callback(). # the value returned by callback().
@ -680,6 +691,7 @@ class IocpProactor:
already be signalled (pending in the proactor event queue). It is also already be signalled (pending in the proactor event queue). It is also
safe if the event is never signalled (because it was cancelled). safe if the event is never signalled (because it was cancelled).
""" """
self._check_closed()
self._unregistered.append(ov) self._unregistered.append(ov)
def _get_accept_socket(self, family): def _get_accept_socket(self, family):
@ -749,6 +761,10 @@ class IocpProactor:
self._stopped_serving.add(obj) self._stopped_serving.add(obj)
def close(self): def close(self):
if self._iocp is None:
# already closed
return
# Cancel remaining registered operations. # Cancel remaining registered operations.
for address, (fut, ov, obj, callback) in list(self._cache.items()): for address, (fut, ov, obj, callback) in list(self._cache.items()):
if fut.cancelled(): if fut.cancelled():
@ -771,14 +787,25 @@ class IocpProactor:
context['source_traceback'] = fut._source_traceback context['source_traceback'] = fut._source_traceback
self._loop.call_exception_handler(context) self._loop.call_exception_handler(context)
# Wait until all cancelled overlapped complete: don't exit with running
# overlapped to prevent a crash. Display progress every second if the
# loop is still running.
msg_update = 1.0
start_time = time.monotonic()
next_msg = start_time + msg_update
while self._cache: while self._cache:
if not self._poll(1): if next_msg <= time.monotonic():
logger.debug('taking long time to close proactor') logger.debug('%r is running after closing for %.1f seconds',
self, time.monotonic() - start_time)
next_msg = time.monotonic() + msg_update
# handle a few events, or timeout
self._poll(msg_update)
self._results = [] self._results = []
if self._iocp is not None:
_winapi.CloseHandle(self._iocp) _winapi.CloseHandle(self._iocp)
self._iocp = None self._iocp = None
def __del__(self): def __del__(self):
self.close() self.close()

View File

@ -0,0 +1,3 @@
:mod:`asyncio`: Enhance ``IocpProactor.close()`` log: wait 1 second before
the first log, then log every second. Log also the number of seconds since
``close()`` was called.