Merge lp:~jameinel/bzr/2.0.4-update-exception-495023 into lp:bzr/2.0

Proposed by John A Meinel
Status: Merged
Approved by: Andrew Bennetts
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~jameinel/bzr/2.0.4-update-exception-495023
Merge into: lp:bzr/2.0
Diff against target: 71 lines (+26/-2)
3 files modified
NEWS (+4/-0)
bzrlib/_dirstate_helpers_pyx.pyx (+2/-1)
bzrlib/tests/test__dirstate_helpers.py (+20/-1)
To merge this branch: bzr merge lp:~jameinel/bzr/2.0.4-update-exception-495023
Reviewer Review Type Date Requested Status
Andrew Bennetts Approve
Review via email: mp+16805@code.launchpad.net
To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

This is a quick-and-simple fix for bug #495023. It just changes the _update_current_block function so that it can raise an exception. The bug is that we were using a C helper that didn't define an 'except' mechanism, which then called into regular python code. Any pending exception probably gets triggered at that point, but we then suppressed it.

Andrew's fix probably helps, in that we poll for pending exceptions earlier. However, it doesn't get at the root, just shrinks the available window.

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

 review approve
 merge approved

John A Meinel wrote:
> John A Meinel has proposed merging lp:~jameinel/bzr/2.0.4-update-exception-495023 into lp:bzr/2.0.
>
> Requested reviews:
> bzr-core (bzr-core)
> Related bugs:
> #495023 Interrupting commit to smart server sometimes removes files
> https://bugs.launchpad.net/bugs/495023
>
>
> This is a quick-and-simple fix for bug #495023. It just changes the
> _update_current_block function so that it can raise an exception. The bug is
> that we were using a C helper that didn't define an 'except' mechanism, which
> then called into regular python code. Any pending exception probably gets
> triggered at that point, but we then suppressed it.
>
> Andrew's fix probably helps, in that we poll for pending exceptions earlier.
> However, it doesn't get at the root, just shrinks the available window.

This patch is good, but I wonder if it goes far enough? It's closer to
the root, but I don't think it's there yet. I think it's good enough to
solve the immediate bug, so probably fine to land as is, but there's
more work to do:

$ grep -Irn 'cdef void[^*]*(' bzrlib/*.pyx
bzrlib/_dirstate_helpers_pyx.pyx:1376: cdef void _gather_result_for_consistency(self, result):
bzrlib/_dirstate_helpers_pyx.pyx:1395: cdef void _update_current_block(self):
bzrlib/_knit_load_data_pyx.pyx:100: cdef void validate(self):

So there are two other Pyrex functions that might suppress exceptions.
The _knit_load_data_pyx one is particularly ironic:

    cdef void validate(self):
        if not PyDict_CheckExact(self.cache):
            raise TypeError('kndx._cache must be a python dict')
        if not PyList_CheckExact(self.history):
            raise TypeError('kndx._history must be a python list')

Similiarly, grepping for __Pyx_WriteUnraisable in *.c files reveals that:

 * _simple_set_pyx's SimpleSet_Next suppresses errors (it returns int,
   but lacks an except -1 that it ought to have). Normal return values
   are 0 or 1.
 * _known_graph_pyx._MergeSortNode.has_pending_parents: ditto.

So I think we should add a test_source test for pyx files that checks for:

 * cdef void without except.
 * cdef int without except.

And, of course, we should fix the specific instances I just listed :)

> === modified file 'NEWS'
[...]
> +* ``_update_current_block`` no longer suppresses exceptions, so ^C at just
> + the right time will get propagated, rather than silently failing to move
> + the block pointer. (John Arbash Meinel, #495023)

The reporter, Gareth White, has been unusually responsive, patient and
helpful given the difficulty of diagnosing this bug. So I'd be inclined
to take the unusual step of publically thanking him in the NEWS entry.

-Andrew.

review: Approve
Revision history for this message
John A Meinel (jameinel) wrote :
Download full text (3.7 KiB)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andrew Bennetts wrote:
> Review: Approve
> review approve
> merge approved
>
> John A Meinel wrote:
>> John A Meinel has proposed merging lp:~jameinel/bzr/2.0.4-update-exception-495023 into lp:bzr/2.0.
>>
>> Requested reviews:
>> bzr-core (bzr-core)
>> Related bugs:
>> #495023 Interrupting commit to smart server sometimes removes files
>> https://bugs.launchpad.net/bugs/495023
>>
>>
>> This is a quick-and-simple fix for bug #495023. It just changes the
>> _update_current_block function so that it can raise an exception. The bug is
>> that we were using a C helper that didn't define an 'except' mechanism, which
>> then called into regular python code. Any pending exception probably gets
>> triggered at that point, but we then suppressed it.
>>
>> Andrew's fix probably helps, in that we poll for pending exceptions earlier.
>> However, it doesn't get at the root, just shrinks the available window.
>
> This patch is good, but I wonder if it goes far enough? It's closer to
> the root, but I don't think it's there yet. I think it's good enough to
> solve the immediate bug, so probably fine to land as is, but there's
> more work to do:
>
> $ grep -Irn 'cdef void[^*]*(' bzrlib/*.pyx
> bzrlib/_dirstate_helpers_pyx.pyx:1376: cdef void _gather_result_for_consistency(self, result):
> bzrlib/_dirstate_helpers_pyx.pyx:1395: cdef void _update_current_block(self):
> bzrlib/_knit_load_data_pyx.pyx:100: cdef void validate(self):
>
> So there are two other Pyrex functions that might suppress exceptions.
> The _knit_load_data_pyx one is particularly ironic:
>
> cdef void validate(self):
> if not PyDict_CheckExact(self.cache):
> raise TypeError('kndx._cache must be a python dict')
> if not PyList_CheckExact(self.history):
> raise TypeError('kndx._history must be a python list')

Nice. :) I do wonder if it is finally time to just nuke this extension.
I know Robert wanted to get rid of it a long time ago, but at that point
was just when 0.92 was introduced, which seemed a bit early.

>
> Similiarly, grepping for __Pyx_WriteUnraisable in *.c files reveals that:
>
> * _simple_set_pyx's SimpleSet_Next suppresses errors (it returns int,
> but lacks an except -1 that it ought to have). Normal return values
> are 0 or 1.
> * _known_graph_pyx._MergeSortNode.has_pending_parents: ditto.
>
> So I think we should add a test_source test for pyx files that checks for:
>
> * cdef void without except.
> * cdef int without except.
>
> And, of course, we should fix the specific instances I just listed :)

Any cdef function that has a defined return value has to have except
given. Only ones that return 'object' (which is also ones that don't
have a defined return) auto propagate exceptions.

However, some *cannot* raise exceptions and the overhead of checking the
return value is unwanted.

Manually going through the code and evaluating each 'cdef' function
would be reasonable.

>
>> === modified file 'NEWS'
> [...]
>> +* ``_update_current_block`` no longer suppresses exceptions, so ^C at just
>> + the right time will get propagated, rather than s...

Read more...

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

...

> This patch is good, but I wonder if it goes far enough? It's closer to
> the root, but I don't think it's there yet. I think it's good enough to
> solve the immediate bug, so probably fine to land as is, but there's
> more work to do:

So I'll go ahead and land this as is (modulo NEWS, etc). And then I'll
prepare a bigger patch.

For example, _simple_set_pyx.pyx doesn't *exist* in the 2.0 series :).

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAktCaYUACgkQJdeBCYSNAAMIgwCgqmnl6Fh/ZNFhoTS8GwIz2uia
bqoAoISGG2zo75VS0cs8Xc3HHkc3PZ/9
=2j2Y
-----END PGP SIGNATURE-----

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

John A Meinel wrote:
[...]
> However, some *cannot* raise exceptions and the overhead of checking the
> return value is unwanted.

Hmm. I guess what I'd really like is if Pyrex required you to
explicitly opt-out of exception checking, rather than
on-by-default-or-opt-in-depending-on-return-type. e.g. a syntax like
“cdef int frobnicate(...) cannot_raise:”. Absence of cannot_raise would
imply “except *” (or that the function returns object, etc).

I guess we can manually annotate these cases with “# cannot_raise” if we
check for this with test_source.

-Andrew.

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andrew Bennetts wrote:
> John A Meinel wrote:
> [...]
>> However, some *cannot* raise exceptions and the overhead of checking the
>> return value is unwanted.
>
> Hmm. I guess what I'd really like is if Pyrex required you to
> explicitly opt-out of exception checking, rather than
> on-by-default-or-opt-in-depending-on-return-type. e.g. a syntax like
> “cdef int frobnicate(...) cannot_raise:”. Absence of cannot_raise would
> imply “except *” (or that the function returns object, etc).
>
> I guess we can manually annotate these cases with “# cannot_raise” if we
> check for this with test_source.
>
> -Andrew.
>

Yeah, I'm doing something like that in
  lp:///~jameinel/bzr/2.0.4-pyrex-propagation

Even better if we had smarts so that a 'cannot_raise' function could
only call other 'cannot_raise' functions :)

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAktCffEACgkQJdeBCYSNAAOLTACgzSdGvDgSv+4Gc+ugh5cYQLD+
ZdYAoJqXWPq7CIGaecShVH6pqyJRt3X/
=MjOk
-----END PGP SIGNATURE-----

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

John A Meinel wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
>
> ...
>
> > This patch is good, but I wonder if it goes far enough? It's closer to
> > the root, but I don't think it's there yet. I think it's good enough to
> > solve the immediate bug, so probably fine to land as is, but there's
> > more work to do:
>
> So I'll go ahead and land this as is (modulo NEWS, etc). And then I'll
> prepare a bigger patch.

Thanks, that was my intent. Sorry for not being clearer about that.

> For example, _simple_set_pyx.pyx doesn't *exist* in the 2.0 series :).

Right :)

-Andrew.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2009-12-23 02:19:04 +0000
3+++ NEWS 2010-01-04 22:25:21 +0000
4@@ -42,6 +42,10 @@
5 changed underneath it (like another autopack). Now concurrent
6 autopackers will properly succeed. (John Arbash Meinel, #495000)
7
8+* ``_update_current_block`` no longer suppresses exceptions, so ^C at just
9+ the right time will get propagated, rather than silently failing to move
10+ the block pointer. (John Arbash Meinel, Gareth White, #495023)
11+
12 Improvements
13 ************
14
15
16=== modified file 'bzrlib/_dirstate_helpers_pyx.pyx'
17--- bzrlib/_dirstate_helpers_pyx.pyx 2009-09-30 05:58:45 +0000
18+++ bzrlib/_dirstate_helpers_pyx.pyx 2010-01-04 22:25:21 +0000
19@@ -1392,7 +1392,7 @@
20 # provide.
21 self.search_specific_file_parents.add('')
22
23- cdef void _update_current_block(self):
24+ cdef int _update_current_block(self) except -1:
25 if (self.block_index < len(self.state._dirblocks) and
26 osutils.is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
27 self.current_block = self.state._dirblocks[self.block_index]
28@@ -1401,6 +1401,7 @@
29 else:
30 self.current_block = None
31 self.current_block_list = None
32+ return 0
33
34 def __next__(self):
35 # Simple thunk to allow tail recursion without pyrex confusion
36
37=== modified file 'bzrlib/tests/test__dirstate_helpers.py'
38--- bzrlib/tests/test__dirstate_helpers.py 2009-06-22 15:39:42 +0000
39+++ bzrlib/tests/test__dirstate_helpers.py 2010-01-04 22:25:21 +0000
40@@ -1,4 +1,4 @@
41-# Copyright (C) 2007, 2008 Canonical Ltd
42+# Copyright (C) 2007, 2008, 2009 Canonical Ltd
43 #
44 # This program is free software; you can redistribute it and/or modify
45 # it under the terms of the GNU General Public License as published by
46@@ -1295,6 +1295,25 @@
47 tree.unlock()
48 self.assertEqual(sorted(expected), sorted(file_ids))
49
50+ def test_exceptions_raised(self):
51+ # This is a direct test of bug #495023, it relies on osutils.is_inside
52+ # getting called in an inner function. Which makes it a bit brittle,
53+ # but at least it does reproduce the bug.
54+ def is_inside_raises(*args, **kwargs):
55+ raise RuntimeError('stop this')
56+ tree = self.make_branch_and_tree('tree')
57+ self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
58+ 'tree/dir2/', 'tree/dir2/sub2'])
59+ tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
60+ tree.commit('first commit')
61+ tree.lock_read()
62+ self.addCleanup(tree.unlock)
63+ basis_tree = tree.basis_tree()
64+ orig = osutils.is_inside
65+ self.addCleanup(setattr, osutils, 'is_inside', orig)
66+ osutils.is_inside = is_inside_raises
67+ self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
68+
69 def test_simple_changes(self):
70 tree = self.make_branch_and_tree('tree')
71 self.build_tree(['tree/file'])

Subscribers

People subscribed via source and target branches