Skip to content

Commit

Permalink
Recognize ERROR_INVALID_HANDLE as indicating the IOCP was closed
Browse files Browse the repository at this point in the history
There's a race condition when we signal the IOCP thread to exit by
closing the IOCP: the thread might be blocked in
GetQueuedCompletionStatusEx, or it might (rarely) be in the rest of
the loop. The effect of this is that there are two possible error
messages we might get that both indicate that the IOCP was
closed. Before we were only detecting one of them; now we detect
both.

This has the side-effect of making it so that during the test suite we
deterministically only take the regular-exit path, rather than
sometimes taking the blow-up-on-unrecognized-error path. Which is
good because codecov was getting cranky about that non-deterministic
coverage and yelling at people for no reason (see python-triogh-81).
  • Loading branch information
njsmith committed Mar 11, 2017
1 parent 08bf8b6 commit cd8bdc2
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 1 deletion.
14 changes: 13 additions & 1 deletion trio/_core/_io_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,18 @@ def do_select():
queue.put_nowait(info)

def _iocp_thread_fn(self):
# This thread sits calling GetQueuedCompletionStatusEx forever. To
# signal that it should shut down, the main thread just closes the
# IOCP, which causes GetQueuedCompletionStatusEx to return with an
# error:
IOCP_CLOSED_ERRORS = {
# If the IOCP is closed while we're blocked in
# GetQueuedCompletionStatusEx, then we get this error:
ErrorCodes.ERROR_ABANDONED_WAIT_0,
# If the IOCP is already closed when we initiate a
# GetQueuedCompletionStatusEx, then we get this error:
ErrorCodes.ERROR_INVALID_HANDLE,
}
while True:
max_events = 1
batch = ffi.new("OVERLAPPED_ENTRY[]", max_events)
Expand All @@ -277,7 +289,7 @@ def _iocp_thread_fn(self):
_check(kernel32.GetQueuedCompletionStatusEx(
self._iocp, batch, max_events, received, 0xffffffff, 0))
except OSError as exc:
if exc.winerror == ErrorCodes.ERROR_ABANDONED_WAIT_0:
if exc.winerror in IOCP_CLOSED_ERRORS:
# The IOCP handle was closed; time to shut down.
return
else:
Expand Down
1 change: 1 addition & 0 deletions trio/_core/_windows_cffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,4 @@ class ErrorCodes(enum.IntEnum):
ERROR_IO_PENDING = 997
ERROR_OPERATION_ABORTED = 995
ERROR_ABANDONED_WAIT_0 = 735
ERROR_INVALID_HANDLE = 6

0 comments on commit cd8bdc2

Please sign in to comment.