简体   繁体   中英

Signal handling in Pylons

I have a pylons project where I need to update some in-memory structures periodically. This should be done on-demand. I decided to come up with a signal handler for this. User sends SIGUSR1 to the main pylons thread and it is handled by the project.

This works except after handling the signal, the server crashes with following exception:

File "/usr/lib/python2.6/SocketServer.py", line 264, in handle_request
   fd_sets = select.select([self], [], [], timeout)
select.error: (4, 'Interrupted system call')

Is it possible to fix this?

TIA.

Yes, it is possible, but not easy using the stock Python libraries. This is due to Python translating all OS errors to exceptions. However, EINTR should really cause a retry of the system call used. Whenever you start using signals in Python you will see this error sporadically.

I have code that fixes this (SafeSocket), by forking Python modules and adding that functionality. But it needs to be added everywhere system calls are used. So it's possible, but not easy. But you can use my open-source code, it may save you years of work. ;-)

The basic pattern is this (implemented as a system call decorator):

# decorator to make system call methods safe from EINTR
def systemcall(meth):
    # have to import this way to avoid a circular import
    from _socket import error as SocketError
    def systemcallmeth(*args, **kwargs):
        while 1:
            try:
                    rv = meth(*args, **kwargs)
            except EnvironmentError as why:
                if why.args and why.args[0] == EINTR:
                    continue
                else:
                    raise
            except SocketError as why:
                if why.args and why.args[0] == EINTR:
                    continue
                else:
                    raise
            else:
                break
        return rv
    return systemcallmeth

You could also just use that around your select call.

A fix, at least works for me, from an 12 year old python-dev list post

    while True:
        try:
            readable, writable, exceptional = select.select(inputs, outputs, inputs, timeout)
        except select.error, v:
            if v[0] != errno.EINTR: raise
        else: break

The details of the actual select line isn't important... your "fd_sets = select.select([self], [], [], timeout)" line should work exactly the same.

The important bit is to check for EINTR and retry/loop if that is caught. Oh, and don't forget to import errno.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM