GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — [asyncio][FreeBSD] _UnixWritePipeTransport.write() may raise BrokenPipeError on FreeBSD · Issue #109757 · python/cpython Open Labels 3.12only security fixesonly security fixes3.13only security fixesonly security fixesOS-freebsdstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error DescriptionBug reportBug description:Earlier discussion: #109710 (comment) This issue is specific to some systems with kqueue. I can reproduce on FreeBSD, and according to the internet it should be reproduced on older macOS versions (10.11) and on OpenBSD/NetBSD (with a different error code). A current version of macOS (13) is not affected. Minimal snippet to explain the root issue: import os, selectors sel = selectors.DefaultSelector() read_fd, write_fd = os.pipe() # Close one end of the pipe os.close(read_fd) # Register the other end of the pipe sel.register(write_fd, selectors.EVENT_WRITE) Running this code on Linux or macOS will not raise any exception. The last call to asyncio does not account for this platform difference. This reproducer is a bit contrived because it needs to trigger a race condition1: import asyncio from itertools import count import os from threading import Thread class MyPipeProtocol(asyncio.BaseProtocol): def __init__(self): self.is_connection_lost = False def connection_lost(self, exc): self.is_connection_lost = True async def broken_pipe_repro(): read_fd, write_fd = os.pipe() os.set_blocking(write_fd, False) write_file = open(write_fd, "wb") loop = asyncio.get_running_loop() transport, proto = await loop.connect_write_pipe(MyPipeProtocol, write_file) # Pass one end of the pipe to another thread that will eventually close it t = Thread(target=lambda: os.close(read_fd)) t.start() try: # This line will never fail on Linux/macOS, but might on other BSDs: transport.write(b"ping" * 65536) await asyncio.sleep(0) assert proto.is_connection_lost finally: t.join() transport.close() async def amain(): for i in count(): if i % 1000 == 0: print(i) await broken_pipe_repro() if __name__ == "__main__": asyncio.run(amain()) On most platform this will run indefinitely. On FreeBSD, after a few run I get a traceback: The issue here is that The current implementation of I found prior discussion of the equivalent issue in Tokio. In their case, they decided to solve this at an abstraction level that is closer to It can also be caught in asyncio (at some layer, either in selector event loops, or in code that calls I can make a PR if this is accepted as a bug. cc @vstinner Edit note: my mistake, the error is not raised when registering the read end of a pipe, only the write end. CPython versions tested on:3.12, CPython main branch Operating systems tested on:Other Footnotes
Activity
MetadataMetadataAssigneesLabels3.12only security fixesonly security fixes3.13only security fixesonly security fixesOS-freebsdstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error Projects
MilestoneRelationshipsDevelopmentIssue actions |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |