#29 PosixSerial.read() should "ignore" errno.EINTR

v2.7
closed-fixed
None
5
2013-10-11
2013-03-21
d4or
No

File "/usr/lib/python2.7/site-packages/serial/serialposix.py", line 446, in read
ready,_,_ = select.select([self.fd],[],[], self._timeout)
error: (4, 'Interrupted system call')

This error occurs, when pyserial tries to read data from a serial device, and select returns an error.
It seems, that this can be fixed with the code from http://stackoverflow.com/questions/5633067/signal-handling-in-pylons/10306378#10306378 .

So I propose to replace serial/serialposix.py, line 446:

ready,_,_ = select.select([self.fd],[],[], self._timeout)

with

while True:
try:
ready,_,_ = select.select([self.fd],[],[], self._timeout)
except select.error, v:
if v[0] != errno.EINTR: raise
else: break

Perhaps it could be wise to add a time.sleep(0.001), to prevent 100% CPU usage, if the Interrupted system call error stays for a longer period. On the other hand this would slow down the process, if the error occurs only once.

PS: The Interrupted system call error for example occurs when opening a QFileDialog with PyQt4.

Discussion

  • d4or

    d4or - 2013-03-21

    Updated file

     
  • Luke Kenneth Casson Leighton

    or this:

    # select based implementation, proved to work on many systems
    def read(self, size=1):
        """Read size bytes from the serial port. If a timeout is set it may
           return less characters as requested. With no timeout it will block
           until the requested number of bytes is read."""
        if self.fd is None: raise portNotOpenError
        read = bytearray()
        while len(read) < size:
            ready,_,_ = select.select([self.fd],[],[], self._timeout)
            # If select was used with a timeout, and the timeout occurs, it
            # returns with empty lists -> thus abort read operation.
            # For timeout == 0 (non-blocking operation) also abort when there
            # is nothing to read.
            if not ready:
                break   # timeout
            succeeded = False
            while not succeeded:
                try:
                    buf = os.read(self.fd, size-len(read))
                    succeeded = True
                except OSError, v:
                    if v.errno != errno.EAGAIN:
                        raise SerialException('read failed: %s' % (v,))
                    pass
    

    this is an important error case to catch, because for example a modem being tested on will fail unexpectedly when it should not, just because it happens to be busy at that exact moment.

     
  • Chris Liechti

    Chris Liechti - 2013-10-11

    implemented a bit differently (put the try-except around the complete read block, no extra while loop needed)

     
  • Chris Liechti

    Chris Liechti - 2013-10-11
    • status: open --> closed-fixed
    • assigned_to: Chris Liechti
    • Group: --> v2.7
     

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks