Forked from njsmith/blocking-read-with-timeout.py
Created
September 29, 2020 14:28
-
-
Save jab/2e85c51f0be6395b54476cd8ac8bbac3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Ideally, we would manage async access to stdin/stdout/stderr *without* | |
# setting them to non-blocking mode, because that can break other processes. | |
# (See https://github.com/python-trio/trio/issues/174 for much more detail.) | |
# Of course we can call read/write in a separate thread, but then we lose | |
# cancellation support. | |
# This file demonstrates a weird hack to make blocking read/write cancellable, | |
# and thus at least theoretically possible to integrate into Trio as ordinary | |
# first-class operations. | |
# I blame @geofft. | |
# This only works on Linux; possibly it only works on glibc. | |
import trio | |
import os | |
import socket | |
import errno | |
bad_socket = socket.socket() | |
class BlockingReadTimeoutError(Exception): | |
pass | |
async def blocking_read_with_timeout(fd, count, timeout): | |
print("reading from fd", fd) | |
cancel_requested = False | |
async def kill_it_after_timeout(new_fd): | |
print("sleeping") | |
await trio.sleep(timeout) | |
print("breaking the fd") | |
os.dup2(bad_socket.fileno(), new_fd, inheritable=False) | |
# MAGIC | |
print("setuid(getuid())") | |
os.setuid(os.getuid()) | |
nonlocal cancel_requested | |
cancel_requested = True | |
new_fd = os.dup(fd) | |
print("working fd is", new_fd) | |
try: | |
async with trio.open_nursery() as nursery: | |
nursery.start_soon(kill_it_after_timeout, new_fd) | |
try: | |
data = await trio.run_sync_in_worker_thread(os.read, new_fd, count) | |
except OSError as exc: | |
if cancel_requested and exc.errno == errno.ENOTCONN: | |
# Call was successfully cancelled. In a real version we'd | |
# integrate properly with trio's cancellation tools; here | |
# we'll just raise an arbitrary error. | |
raise BlockingReadTimeoutError from None | |
print("got", data) | |
nursery.cancel_scope.cancel() | |
return data | |
finally: | |
os.close(new_fd) | |
trio.run(blocking_read_with_timeout, 0, 10, 2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment