-
-
Notifications
You must be signed in to change notification settings - Fork 387
Implement cancelable WaitForSingleObject for Windows #575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
a10cc94
93313f6
c6a35cd
b047a45
58352ad
f124302
1c0cbec
7e72d42
c750f38
5795d18
197d61d
c77e24c
502f964
1135421
3c7bb5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add ``trio.hazmat.WaitForSingleObject()`` async function to await Windows handles. | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import os | ||
|
|
||
| import pytest | ||
|
|
||
| on_windows = (os.name == "nt") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| from . import _timeouts | ||
| from . import _core | ||
| from ._threads import run_sync_in_worker_thread | ||
| from ._core._windows_cffi import ffi, kernel32, ErrorCodes, raise_winerror, _handle | ||
|
|
||
| __all__ = ["WaitForSingleObject"] | ||
|
|
||
|
|
||
| class StubLimiter: | ||
| def release_on_behalf_of(self, x): | ||
| pass | ||
|
|
||
| async def acquire_on_behalf_of(self, x): | ||
| pass | ||
|
|
||
|
|
||
| async def WaitForSingleObject(obj): | ||
| """Async and cancellable variant of kernel32.WaitForSingleObject(). Windows only. | ||
|
|
||
| Args: | ||
| handle: A win32 handle in the form of an int or cffi HANDLE object. | ||
|
|
||
| """ | ||
| # Allow ints or whatever we can convert to a win handle | ||
| handle = _handle(obj) | ||
|
|
||
| # Quick check; we might not even need to spawn a thread. The zero | ||
| # means a zero timeout; this call never blocks. We also exit here | ||
| # if the handle is already closed for some reason. | ||
| retcode = kernel32.WaitForSingleObject(handle, 0) | ||
| if retcode == ErrorCodes.WAIT_FAILED: | ||
| raise_winerror() | ||
| elif retcode != ErrorCodes.WAIT_TIMEOUT: | ||
| return | ||
|
|
||
| # Wait for a thread that waits for two handles: the handle plus a handle | ||
| # that we can use to cancel the thread. | ||
| cancel_handle = kernel32.CreateEventA(ffi.NULL, True, False, ffi.NULL) | ||
| try: | ||
| await run_sync_in_worker_thread( | ||
| WaitForMultipleObjects_sync, | ||
| handle, | ||
| cancel_handle, | ||
| cancellable=True, | ||
| limiter=StubLimiter(), | ||
| ) | ||
| finally: | ||
| # Clean up our cancel handle. In case we get here because this task was | ||
| # cancelled, we also want to set the cancel_handle to stop the thread. | ||
| kernel32.SetEvent(cancel_handle) | ||
| kernel32.CloseHandle(cancel_handle) | ||
|
|
||
|
|
||
| def WaitForMultipleObjects_sync(*handles): | ||
| """Wait for any of the given Windows handles to be signaled. | ||
|
|
||
| """ | ||
| n = len(handles) | ||
| handle_arr = ffi.new("HANDLE[{}]".format(n)) | ||
| for i in range(n): | ||
| handle_arr[i] = handles[i] | ||
| timeout = 0xffffffff # INFINITE | ||
| retcode = kernel32.WaitForMultipleObjects( | ||
| n, handle_arr, False, timeout | ||
| ) # blocking | ||
| if retcode == ErrorCodes.WAIT_FAILED: | ||
| raise_winerror() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,11 @@ | ||
| """ | ||
| This namespace represents low-level functionality not intended for daily use, | ||
| but useful for extending Trio's functionality. It is the union of | ||
| a subset of trio/_core/ and some things from trio/*.py. | ||
| """ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd drop the second sentence here, because the intended audience for a public module docstring is the users, and it's none of their business which files we put things in internally :-) |
||
|
|
||
| import sys | ||
|
|
||
| # These are all re-exported from trio._core. See comments in trio/__init__.py | ||
| # for details. To make static analysis easier, this lists all possible | ||
| # symbols, and then we prune some below if they aren't available on this | ||
|
|
@@ -56,3 +64,8 @@ | |
| # who knows. | ||
| remove_from_all = __all__.remove | ||
| remove_from_all(_sym) | ||
|
|
||
| # Import bits from trio/*.py | ||
| if sys.platform.startswith("win"): | ||
| from ._wait_for_object import WaitForSingleObject | ||
| __all__ += ["WaitForSingleObject"] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, we can't use sphinx's
autodocfeature here, because for that sphinx has toimport trioand then look at the function's docstring... and the function doesn't exist on Linux, which is what readthedocs.org uses to build our docs. So, we need to type some actual words into this file :-). (monitor_completion_keyisn't documented because it's an unfinished stub... see the TODO at the top of the section.)I'd move this up to the top of the
Windows-specific APIsection (so above the "TODO", since this function is no longer a TODO!), and then if you scroll up a bit in the file you can see the docs forwait_writableas an example of how to write the docs directly inreference-hazmat.rst. (Unfortunately it works a bit differently than for docstrings, because the sphinx-napoleon extension that knows how to interpret the friendly Google-docstring format only works on actual docstrings; when typing into the .rst file you have to use the lower-level ReST markup directly.)If you want to look at the docs locally, you can do:
and then they're in
docs/build/