• Victor Stinner's avatar
    Issue #23095, asyncio: Rewrite _WaitHandleFuture.cancel() · d0a28dee
    Victor Stinner authored
    This change fixes a race conditon related to _WaitHandleFuture.cancel() leading
    to Python crash or "GetQueuedCompletionStatus() returned an unexpected event"
    logs. Before, the overlapped object was destroyed too early, it was possible
    that the wait completed whereas the overlapped object was already destroyed.
    Sometimes, a different overlapped was allocated at the same address, leading to
    unexpected completition.
    
    _WaitHandleFuture.cancel() now waits until the wait is cancelled to clear its
    reference to the overlapped object. To wait until the cancellation is done,
    UnregisterWaitEx() is used with an event instead of UnregisterWait().
    
    To wait for this event, a new _WaitCancelFuture class was added. It's a
    simplified version of _WaitCancelFuture. For example, its cancel() method calls
    UnregisterWait(), not UnregisterWaitEx(). _WaitCancelFuture should not be
    cancelled.
    
    The overlapped object is kept alive in _WaitHandleFuture until the wait is
    unregistered.
    
    Other changes:
    
    * Add _overlapped.UnregisterWaitEx()
    * Remove fast-path in IocpProactor.wait_for_handle() to immediatly set the
      result if the wait already completed. I'm not sure that it's safe to
      call immediatly UnregisterWaitEx() before the completion was signaled.
    * Add IocpProactor._unregistered() to forget an overlapped which may never be
      signaled, but may be signaled for the next loop iteration. It avoids to
      block forever IocpProactor.close() if a wait was cancelled, and it may also
      avoid some "... unexpected event ..." warnings.
    d0a28dee
windows_events.py 26.8 KB