Description
Bug report
Bug description:
The documentation of class io.RawIOBase claims that the 'read' method, when called with a specified transfer size, will perform only one system call ("Otherwise, only one system call is ever made."). However, in these circumstances, it relies on the 'readinto' implementation of a particular RawIOBase subclass. In the case of FileIO the 'readinto' method internally calls '_Py_read' (from fileutils.c) which contains a while loop around the 'read' system call, repeating it if it is interrupted by a signal and the current thread cannot handle the interrupt.
This looping behavior is surely convenient for the user, but it is also troubling in some corner cases. I work with a Linux device driver having a blocking 'read' method, which can only be interrupted by a signal. There are several data streams to capture, so I delegate them to different threads. When I have to interrupt the read (e.g. the machine readout trigger has not arrived) I send the signal to those threads (signal.pthread_kill). I see the driver intercepting it and aborting the transfer, then immediately the '_Py_read' repeats the system call starting another read and, in turn, causing my 'read' call never to return.
CPython versions tested on:
3.10
Operating systems tested on:
Linux
Activity
Ryan-Carrier commentedon May 1, 2024
I am working on this issue.
cmaloney commentedon Apr 30, 2025
The retry loop was added for PEP-475 in Python 3.5. The docs should be updated to match the current behavior, I have been working on that in the implementation comments, will work on the more general I/O docs shortly. In particular,
read()
without a size will read until a zero-length return from theread
underlying system call. The code comments in https://github.com/python/cpython/pull/129012/files are up to date formain
.OlekTk commentedon May 19, 2025
Well, if this is a desired behavior, then there is probably no issue anymore.
In case anyone finds this discussion looking for a solution to a similar problem, here is how to produce an interruptible 'read' syscall:
cmaloney commentedon May 19, 2025
Another option is to raise an exception in the signal handler which will cause a "blocking" read/write to exit (https://docs.python.org/3/library/signal.html#execution-of-python-signal-handlers, write cpython/Python/fileutils.c at 7bb1e1a23634bae81bf76fdb34e9f9f7e59b3793 · python/cpython · GitHub, read cpython/Python/fileutils.c at 7bb1e1a23634bae81bf76fdb34e9f9f7e59b3793 · python/cpython · GitHub), should be able to use that to exit reliably on a signal (Signal handler → exception).