@@ -42,16 +42,12 @@ def __init__(self, ov, *, loop=None):
4242 del self ._source_traceback [- 1 ]
4343 self ._ov = ov
4444
45- def __repr__ (self ):
46- info = [ self . _state . lower ()]
45+ def _repr_info (self ):
46+ info = super (). _repr_info ()
4747 if self ._ov is not None :
4848 state = 'pending' if self ._ov .pending else 'completed'
49- info .append ('overlapped=<%s, %#x>' % (state , self ._ov .address ))
50- if self ._state == futures ._FINISHED :
51- info .append (self ._format_result ())
52- if self ._callbacks :
53- info .append (self ._format_callbacks ())
54- return '<%s %s>' % (self .__class__ .__name__ , ' ' .join (info ))
49+ info .insert (1 , 'overlapped=<%s, %#x>' % (state , self ._ov .address ))
50+ return info
5551
5652 def _cancel_overlapped (self ):
5753 if self ._ov is None :
@@ -85,8 +81,14 @@ def set_result(self, result):
8581class _WaitHandleFuture (futures .Future ):
8682 """Subclass of Future which represents a wait handle."""
8783
88- def __init__ (self , handle , wait_handle , * , loop = None ):
84+ def __init__ (self , iocp , ov , handle , wait_handle , * , loop = None ):
8985 super ().__init__ (loop = loop )
86+ if self ._source_traceback :
87+ del self ._source_traceback [- 1 ]
88+ # iocp and ov are only used by cancel() to notify IocpProactor
89+ # that the wait was cancelled
90+ self ._iocp = iocp
91+ self ._ov = ov
9092 self ._handle = handle
9193 self ._wait_handle = wait_handle
9294
@@ -95,19 +97,16 @@ def _poll(self):
9597 return (_winapi .WaitForSingleObject (self ._handle , 0 ) ==
9698 _winapi .WAIT_OBJECT_0 )
9799
98- def __repr__ (self ):
99- info = [self ._state .lower ()]
100+ def _repr_info (self ):
101+ info = super ()._repr_info ()
102+ info .insert (1 , 'handle=%#x' % self ._handle )
100103 if self ._wait_handle :
101- state = 'pending' if self ._poll () else 'completed'
102- info .append ('wait_handle=<%s, %#x>' % (state , self ._wait_handle ))
103- info .append ('handle=<%#x>' % self ._handle )
104- if self ._state == futures ._FINISHED :
105- info .append (self ._format_result ())
106- if self ._callbacks :
107- info .append (self ._format_callbacks ())
108- return '<%s %s>' % (self .__class__ .__name__ , ' ' .join (info ))
109-
110- def _unregister (self ):
104+ state = 'signaled' if self ._poll () else 'waiting'
105+ info .insert (1 , 'wait_handle=<%s, %#x>'
106+ % (state , self ._wait_handle ))
107+ return info
108+
109+ def _unregister_wait (self ):
111110 if self ._wait_handle is None :
112111 return
113112 try :
@@ -117,10 +116,25 @@ def _unregister(self):
117116 raise
118117 # ERROR_IO_PENDING is not an error, the wait was unregistered
119118 self ._wait_handle = None
119+ self ._iocp = None
120+ self ._ov = None
120121
121122 def cancel (self ):
122- self ._unregister ()
123- return super ().cancel ()
123+ result = super ().cancel ()
124+ if self ._ov is not None :
125+ # signal the cancellation to the overlapped object
126+ _overlapped .PostQueuedCompletionStatus (self ._iocp , True ,
127+ 0 , self ._ov .address )
128+ self ._unregister_wait ()
129+ return result
130+
131+ def set_exception (self , exception ):
132+ super ().set_exception (exception )
133+ self ._unregister_wait ()
134+
135+ def set_result (self , result ):
136+ super ().set_result (result )
137+ self ._unregister_wait ()
124138
125139
126140class PipeServer (object ):
@@ -405,7 +419,9 @@ def wait_for_handle(self, handle, timeout=None):
405419 ov = _overlapped .Overlapped (NULL )
406420 wh = _overlapped .RegisterWaitWithQueue (
407421 handle , self ._iocp , ov .address , ms )
408- f = _WaitHandleFuture (handle , wh , loop = self ._loop )
422+ f = _WaitHandleFuture (self ._iocp , ov , handle , wh , loop = self ._loop )
423+ if f ._source_traceback :
424+ del f ._source_traceback [- 1 ]
409425
410426 def finish_wait_for_handle (trans , key , ov ):
411427 # Note that this second wait means that we should only use
@@ -414,12 +430,17 @@ def finish_wait_for_handle(trans, key, ov):
414430 # or semaphores are not. Also note if the handle is
415431 # signalled and then quickly reset, then we may return
416432 # False even though we have not timed out.
433+ return f ._poll ()
434+
435+ if f ._poll ():
417436 try :
418- return f ._poll ()
419- finally :
420- f ._unregister ()
437+ result = f ._poll ()
438+ except OSError as exc :
439+ f .set_exception (exc )
440+ else :
441+ f .set_result (result )
421442
422- self ._cache [ov .address ] = (f , ov , None , finish_wait_for_handle )
443+ self ._cache [ov .address ] = (f , ov , 0 , finish_wait_for_handle )
423444 return f
424445
425446 def _register_with_iocp (self , obj ):
@@ -438,6 +459,8 @@ def _register(self, ov, obj, callback,
438459 # operation when it completes. The future's value is actually
439460 # the value returned by callback().
440461 f = _OverlappedFuture (ov , loop = self ._loop )
462+ if f ._source_traceback :
463+ del f ._source_traceback [- 1 ]
441464 if not ov .pending and not wait_for_post :
442465 # The operation has completed, so no need to postpone the
443466 # work. We cannot take this short cut if we need the
@@ -484,10 +507,13 @@ def _poll(self, timeout=None):
484507 ms = math .ceil (timeout * 1e3 )
485508 if ms >= INFINITE :
486509 raise ValueError ("timeout too big" )
510+
487511 while True :
488512 status = _overlapped .GetQueuedCompletionStatus (self ._iocp , ms )
489513 if status is None :
490514 return
515+ ms = 0
516+
491517 err , transferred , key , address = status
492518 try :
493519 f , ov , obj , callback = self ._cache .pop (address )
@@ -504,7 +530,6 @@ def _poll(self, timeout=None):
504530 # handle which should be closed to avoid a leak.
505531 if key not in (0 , _overlapped .INVALID_HANDLE_VALUE ):
506532 _winapi .CloseHandle (key )
507- ms = 0
508533 continue
509534
510535 if obj in self ._stopped_serving :
@@ -520,7 +545,6 @@ def _poll(self, timeout=None):
520545 else :
521546 f .set_result (value )
522547 self ._results .append (f )
523- ms = 0
524548
525549 def _stop_serving (self , obj ):
526550 # obj is a socket or pipe handle. It will be closed in
0 commit comments