@@ -760,6 +760,46 @@ def _get_accept_socket(self, family):
760760 s .settimeout (0 )
761761 return s
762762
763+ def _process_completion_status (self , status ):
764+ """Process a single status from the completion port.
765+
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+ })
780+
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
786+
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
802+
763803 def _poll (self , timeout = None ):
764804 if timeout is None :
765805 ms = INFINITE
@@ -778,39 +818,8 @@ def _poll(self, timeout=None):
778818 break
779819 ms = 0
780820
781- err , transferred , key , address = status
782- try :
783- f , ov , obj , callback = self ._cache .pop (address )
784- except KeyError :
785- if self ._loop .get_debug ():
786- self ._loop .call_exception_handler ({
787- 'message' : ('GetQueuedCompletionStatus() returned an '
788- 'unexpected event' ),
789- 'status' : ('err=%s transferred=%s key=%#x address=%#x'
790- % (err , transferred , key , address )),
791- })
792-
793- # key is either zero, or it is used to return a pipe
794- # handle which should be closed to avoid a leak.
795- if key not in (0 , _overlapped .INVALID_HANDLE_VALUE ):
796- _winapi .CloseHandle (key )
797- continue
798-
799- if obj in self ._stopped_serving :
800- f .cancel ()
801- # Don't call the callback if _register() already read the result or
802- # if the overlapped has been cancelled
803- elif not f .done ():
804- try :
805- value = callback (transferred , key , ov )
806- except OSError as e :
807- f .set_exception (e )
808- self ._results .append (f )
809- else :
810- f .set_result (value )
811- self ._results .append (f )
812- finally :
813- f = None
821+ # gh-154971: split out so custom event loops can call it directly
822+ self ._process_completion_status (status )
814823
815824 # Remove unregistered futures
816825 for ov in self ._unregistered :
0 commit comments