Automatically restarting system calls?

D

Dan Stromberg

I wrote a script(1) replacement in python (http://stromberg.dnsalias.org/
~dstromberg/pypty/), but I'm encountering a problem in it.

I think I know the solution to the problem, but I'd've thought python was
high level enough that this solution isn't required, so I wanted to
inquire about it here.

Specifically, the program has a signal handler for window size changes.
And if the window is resized during an os.write() (for example), I get a
python exception about needing to restart the system call.

In C, I know you're supposed to wrap your system calls with while loops
until you don't get an ERESTART, but does one really need to wrap all of
one's os.write()'s (for example) with such while loops in python?

Thanks!
 
R

Rhamphoryncus

I wrote a script(1) replacement in python (http://stromberg.dnsalias.org/
~dstromberg/pypty/), but I'm encountering a problem in it.

I think I know the solution to the problem, but I'd've thought python was
high level enough that this solution isn't required, so I wanted to
inquire about it here.

Specifically, the program has a signal handler for window size changes.
And if the window is resized during an os.write() (for example), I get a
python exception about needing to restart the system call.

In C, I know you're supposed to wrap your system calls with while loops
until you don't get an ERESTART, but does one really need to wrap all of
one's os.write()'s (for example) with such while loops in python?

Unfortunately, signals are sometimes used to intentionally interrupt
system calls, so we can't always loop on ERESTART.

However, os.write() is a low level API. Maybe file.write() or
socket.send() would be a little more robust?
 
D

Dan Stromberg

Unfortunately, signals are sometimes used to intentionally interrupt
system calls, so we can't always loop on ERESTART.

However, os.write() is a low level API. Maybe file.write() or
socket.send() would be a little more robust?

Hmmm... How about an option with a default value of True or False, that
would control such looping?

BTW, it seems I'm getting EINTR, not ERESTART.

I perhaps could use file.write(), but I'm not confident that's the case,
because I'm using select.select(), and I'm getting EINTR's not just in
os.write() but also select.select() and tty.setraw(), so far.

The worst part is, I don't really know which python functions require
such loops and which don't, or even what those exceptions will look like
- that is, until I see a real world example. So as the errors come up in
real life, I'm wrapping my functions with:

def maybe_restarted_syscall(fn, loop_exc, loop_errnos):
while 1:
try:
result = fn()
except loop_exc, (errno, error_string):
if errno in loop_errnos:
continue
else:
sys.stderr.write('%s: %s\n' % (sys.argv[0], error_string))
raise
else:
break
return result

....and calling it like (for example) :

maybe_restarted_syscall(lambda : select.select([stdin, fd], [], []),
select.error, [ errno.EINTR ])

Is there some way of being a little more proactive with these errors?

Thanks!
 
R

Rhamphoryncus

Unfortunately, signals are sometimes used to intentionally interrupt
system calls, so we can't always loop on ERESTART.
However, os.write() is a low level API. Maybe file.write() or
socket.send() would be a little more robust?

Hmmm... How about an option with a default value of True or False, that
would control such looping?

BTW, it seems I'm getting EINTR, not ERESTART.

I perhaps could use file.write(), but I'm not confident that's the case,
because I'm using select.select(), and I'm getting EINTR's not just in
os.write() but also select.select() and tty.setraw(), so far.

The worst part is, I don't really know which python functions require
such loops and which don't, or even what those exceptions will look like
- that is, until I see a real world example. So as the errors come up in
real life, I'm wrapping my functions with:

def maybe_restarted_syscall(fn, loop_exc, loop_errnos):
while 1:
try:
result = fn()
except loop_exc, (errno, error_string):
if errno in loop_errnos:
continue
else:
sys.stderr.write('%s: %s\n' % (sys.argv[0], error_string))
raise
else:
break
return result

...and calling it like (for example) :

maybe_restarted_syscall(lambda : select.select([stdin, fd], [], []),
select.error, [ errno.EINTR ])

Is there some way of being a little more proactive with these errors?

I can't think of anything. The best way to handle signals is to avoid
them whenever possible - which probably can't work in your case.

You might find a way to get SA_RESTART applied, but it won't work
right for you. Python splits signal handlers into a top half and a
bottom half - the bottom is in C, it's the real signal handler, and
top is in python. To keep things sane for the top half, it delays
until the interpreter is in a sane state (such as between bytecodes).
If you used SA_RESTART the bottom half would still work, but the top
would be delayed indefinitely until something else woke up the
interpreter. In other words, your window likely won't redraw until
the user types something or you print something.
 
H

Hrvoje Niksic

Rhamphoryncus said:
You might find a way to get SA_RESTART applied, but it won't work
right for you. Python splits signal handlers into a top half and a
bottom half - the bottom is in C, it's the real signal handler, and
top is in python. To keep things sane for the top half, it delays
until the interpreter is in a sane state (such as between
bytecodes). If you used SA_RESTART the bottom half would still
work, but the top would be delayed indefinitely until something else
woke up the interpreter. In other words, your window likely won't
redraw until the user types something or you print something.

One possible way to handle this is by implementing a small signal
handler in C. This signal handler can set the signal using
SA_RESTART, getting rid of EINTR, at least for popular syscalls. In
the signal handler it can simply write a byte into a pipe, the other
end of which is monitored by the select() event loop. Python,
presumably sleeping in select(), or about to do that, will immediately
notice the "signal" by the pipe becoming readable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top