Merge lp:~spiv/bzr/i-hate-signals into lp:bzr

Proposed by Andrew Bennetts
Status: Merged
Merged at revision: not available
Proposed branch: lp:~spiv/bzr/i-hate-signals
Merge into: lp:bzr
Diff against target: 51 lines (+23/-7)
2 files modified
NEWS (+6/-0)
bzrlib/osutils.py (+17/-7)
To merge this branch: bzr merge lp:~spiv/bzr/i-hate-signals
Reviewer Review Type Date Requested Status
Martin Packman (community) Approve
bzr-core Pending
Review via email: mp+23004@code.launchpad.net

Description of the change

Python resets the siginterrupt flag when it invokes a pure-python signal handler. So this patch sets it back again.

This seems to be a bug in upstream Python (in their docs if nothing else), I'll submit a bug report soon.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Abstaining so I get CCed until I have a spare moment to look at this properly.

Of somewhat related interest on python-dev today:
<http://mail.python.org/pipermail/python-dev/2010-April/099165.html>
<http://bugs.python.org/issue7978>

review: Abstain
Revision history for this message
Andrew Bennetts (spiv) wrote :

I filed <http://bugs.python.org/issue8354> about this.

I should clarify that this patch doesn't fully workaround that bug, but it helps in the case that there's some IO between delivery of signals (so that my signal handler gets a chance to redo the siginterrupt call that Python undoes). The only relevant signal for current bzr is SIGWINCH, so IO just needs to happen more often than a window resize event, which is plausible — it did fix the immediate issue that I ran into as a user.

Revision history for this message
Martin Packman (gz) wrote :

Okay, been through this, the upstream bug, and the signal module C code.
Diagnosis looks fine, as does the proposed patch to the C signal handler in the Python source, would be good to get that landed.

One thing I'm not clear on is how effective the workaround in this merge will be. As the Python handler won't run till the IO function returns, and we've prevented it returning early, there can (and in common cases will be) whole seconds elapsed before say, `sock.read(65536)` completes. Do window managers fire multiple SIGWINCHes if a terminal emulator is dragged wider, or do they tend to wait for the release?

review: Needs Information
Revision history for this message
Andrew Bennetts (spiv) wrote :

It probably depends on the window manager: some have you drag an outline (or static snapshot) of the window, some do "live" resizing (and some can be configured either way). For me the most common way I resize a terminal is by maximising or unmaximising a GNOME Terminal, which thankfully only seems to trigger one SIGWINCH per (un)maximise.

There can be other ways to trigger a resize too, like opening a second tab in a maximised GNOME Terminal window: the creation of the tab widget consumes some screen real estate, so that's a terminal resize too. Opening a second tab and (un)maximising are relatively frequent operations for me.

I'm sure there are many real-world cases this workaround can't fix, sadly. But I can say that it definitely improved behaviour for me in some real-world cases, e.g. a 'bzr pull' over plain HTTP.

Revision history for this message
Martin Pool (mbp) wrote :

Tangentially, we do have the option to say that sigwinch is just too
much trouble and instead poll the window size when we need it. But
polling every time we update progress may be too often, and doing it
infrequently may cause mess when the window is resized.

Alternatively we could hook that signal from C, and make it just
update a global accessible from python.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Martin Packman (gz) wrote :

Okay, so there are common situations this workaround-for-a-workaround will help with then. Can't test here, but diff looks fine to me. Oh, and I sympathise with the branch name.

review: Approve
Revision history for this message
Andrew Bennetts (spiv) wrote :

Martin P: I think avoiding SIGWINCH, or at least a Python handler for it, is worth exploring. Although there's a reasonable-looking fix attached to the upstream bug, which would make the existing Python SIGWINCH handler just fine.

In the meantime, I'll merge this partial workaround because it is better than the status quo.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2010-04-14 04:35:47 +0000
+++ NEWS 2010-04-14 06:49:37 +0000
@@ -40,6 +40,12 @@
40 which is not installed any more" error.40 which is not installed any more" error.
41 (Martin Pool, James Westby, #528114)41 (Martin Pool, James Westby, #528114)
4242
43* Reset ``siginterrupt`` flag to False every time we handle a signal
44 installed with ``set_signal_handler(..., restart_syscall=True)`` (from
45 ``bzrlib.osutils``. Reduces the likelihood of "Interrupted System Call"
46 errors after two window resizes.
47 (Andrew Bennetts)
48
43* When invoked with a range revision, ``bzr log`` doesn't show revisions49* When invoked with a range revision, ``bzr log`` doesn't show revisions
44 that are not part of the ancestry anymore.50 that are not part of the ancestry anymore.
45 (Vincent Ladeuil, #474807)51 (Vincent Ladeuil, #474807)
4652
=== modified file 'bzrlib/osutils.py'
--- bzrlib/osutils.py 2010-03-26 04:47:45 +0000
+++ bzrlib/osutils.py 2010-04-14 06:49:37 +0000
@@ -1365,15 +1365,25 @@
1365 False)`). May be ignored if the feature is not available on this1365 False)`). May be ignored if the feature is not available on this
1366 platform or Python version.1366 platform or Python version.
1367 """1367 """
1368 old_handler = signal.signal(signum, handler)1368 try:
1369 siginterrupt = signal.siginterrupt
1370 except AttributeError:
1371 # siginterrupt doesn't exist on this platform, or for this version
1372 # of Python.
1373 siginterrupt = lambda signum, flag: None
1369 if restart_syscall:1374 if restart_syscall:
1370 try:1375 def sig_handler(*args):
1371 siginterrupt = signal.siginterrupt1376 # Python resets the siginterrupt flag when a signal is
1372 except AttributeError: # siginterrupt doesn't exist on this platform, or for this version of1377 # received. <http://bugs.python.org/issue8354>
1373 # Python.1378 # As a workaround for some cases, set it back the way we want it.
1374 pass
1375 else:
1376 siginterrupt(signum, False)1379 siginterrupt(signum, False)
1380 # Now run the handler function passed to set_signal_handler.
1381 handler(*args)
1382 else:
1383 sig_handler = handler
1384 old_handler = signal.signal(signum, sig_handler)
1385 if restart_syscall:
1386 siginterrupt(signum, False)
1377 return old_handler1387 return old_handler
13781388
13791389