miccoli / pyownet

Pure python client library for accessing OWFS via owserver protocol.
http://pyownet.readthedocs.io/
GNU Lesser General Public License v3.0
10 stars 6 forks source link

Program aborted with [Errno 4] Interrupted system call #8

Open motoz opened 8 years ago

motoz commented 8 years ago

I'm using a periodic signal handler in my program and bumped into this with another socket connection. Then it occured to me that this could be the cause of the occasional errors I have seen from the pyownet communication.

This short program shows the issue:

from pyownet import protocol
import signal, time

def periodic_signal_handler(signum, frame):
    print 'sig'

signal.signal(signal.SIGALRM, periodic_signal_handler)
signal.setitimer(signal.ITIMER_REAL, 2, 0.01)
p = protocol.proxy('192.168.1.7')

while True:
    p.dir()
    time.sleep(0.2)

typical output:

python errnotest.py 
sig
sig
sig
sig
sig
sig
sig
sig
Traceback (most recent call last):
  File "errnotest.py", line 14, in <module>
    p.dir()
  File "/usr/local/lib/python2.7/dist-packages/pyownet/protocol.py", line 500, in dir
    ret, data = self.sendmess(msg, str2bytez(path), flags)
  File "/usr/local/lib/python2.7/dist-packages/pyownet/protocol.py", line 467, in sendmess
    raise ConnError(*err.args)
pyownet.protocol.ConnError: [Errno 4] Interrupted system call

Some background info I found: http://stackoverflow.com/questions/16094618/python-socket-recv-and-signals http://250bpm.com/blog:12

Seems like python does not reastart the syscall on EINTR even though it should, so it has to be handled in the program by looping on the syscall until it ends with something other than EINTR:

while True:
    try:
        data = s.recv(2048)
    except socket.error, e:
        if e.errno != errno.EINTR:
            raise
    else:
        break

(Ubuntu 14.04 with python 2.7.6)

miccoli commented 8 years ago

This issue is linked to a python "feature" described in PEP 0475. Fortunately this "feature" was declared a bug and corrected in Python 3.5

I plan to keep full support for Python 2.7, so I will fix this issue, but I still have to set a milestone linked to better python version handling.

miccoli commented 8 years ago

Bug confirmed for pyownet ver. 0.9.0 and 0.9.1.dev7 on CPython 2.7.11; bug absent, as expected, on CPython 3.5.1.

miccoli commented 8 years ago

A quick update on this issue.

Unfortunately Python 2.7.11 and Python 3.5.1 have diverged enough to make it unpractical to fix this issue within a Python 2-3 compatible code. Since I've already in my pipeline the complete rewriting of the socket code, solving this issue will not happen really soon now.

For Python2 at present the only possible mitigation is retrying the operation at the application level.

import errno
from pyownet import protocol

owp = protocol.proxy()
while True:
    try:
        res = owp.dir()
        break
    except protocol.ConnError as cerr:
        if cerr.errno == errno.EINTR:
            continue
motoz commented 8 years ago

Retrying at the application level is fine with me, and one more reason to begin porting to python 3 then.