8000 BUG: test, fix PyArray_DiscardWritebackIfCopy refcount issue and docu… by mattip · Pull Request #10824 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: test, fix PyArray_DiscardWritebackIfCopy refcount issue and docu… #10824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 25, 2018

Conversation

mattip
Copy link
Member
@mattip mattip commented Mar 29, 2018

PyArray_DiscardWritebackIfCopy was not properly detaching self from self->base, as a consequence it left a ref to self->base dangling. This tests and fixes the function, and more clearly documents both PyArray_DiscardWritebackIfCopy and PyArray_ResolveWritebackIfCopy

static NPY_INLINE void
PyArray_DiscardWritebackIfCopy(PyArrayObject *arr)
{
if (arr != NULL) {
PyArrayObject_fields *fa = (PyArrayObject_fields *)arr;
if ((PyArray_FLAGS(arr) & NPY_ARRAY_WRITEBACKIFCOPY) ||
(PyArray_FLAGS(arr) & NPY_ARRAY_UPDATEIFCOPY)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs more indentation to separate it from the block. Alignment isn't desirable here.

if ((PyArray_FLAGS(arr) & NPY_ARRAY_WRITEBACKIFCOPY) ||
(PyArray_FLAGS(arr) & NPY_ARRAY_UPDATEIFCOPY)) {
PyArrayObject *base = (PyArrayObject *)PyArray_BASE(arr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't you continue using base?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to do the same thing, just in one step instead of two.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed to be more like PyArray_ResolveWritebackIfCopy which this is meant to be like (except without the PyArray_CopyAnyInto)

@@ -7254,9 +7254,31 @@ def test_view_assign(self):
assert_equal(arr, -100)
# after resolve, the two arrays no longer reference each other
assert_(not arr_wb.ctypes.data == 0)
assert_equal(arr_wb.base, None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, but shouldn't the base point to something valid even if the array is about to be destroyed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

base is meant to be None for an array that owns its data, see documentation for base. The point of the test is to show that the two arrays arr_wb and arr are totally independent after the call to PyArray_ResolveWritebackIfCopy or PyArray_DiscardWritebackIfCopy.

arr_wb[:] = 100
assert_equal(arr, -100)

def test_view_discard_refcount(self):
from numpy.core._multiarray_tests import npy_create_writebackifcopy, npy_discard
arr = np.arange(9).reshape(3, 3).T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line above this.

arr_wb = npy_create_writebackifcopy(arr)
assert_(arr_wb.flags.writebackifcopy)
assert_(arr_wb.base is arr)
arr_wb[:] = -100
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe arr_wb[...] is clearer? The array has multiple dimensions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

# arr remains unchanged after discard
assert_equal(arr, orig)
# after discard, the two arrays no longer reference each other
assert_(not arr_wb.ctypes.data == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not? I assume this is to check that the data pointer still exists, so why not !=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

assert_equal(arr_wb.base, None)
if HAS_REFCOUNT:
assert_equal(arr_cnt, sys.getrefcount(arr))
arr_wb[:] = 100
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above. I don't quite see what this tests except that you can use the array.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comments to try to clarify the intent.

I wanted to test that I can use arr_wb and that after the call to discard or resolve the changes to arr_wb are not transferred to arr`

@mattip
Copy link
Member Author
mattip commented Apr 9, 2018

What is the policy for inclusion in 1.15? This is non-critical but does fix a refcount leak

@charris charris added this to the 1.15.0 release milestone Apr 9, 2018
@charris
Copy link
Member
charris commented Apr 9, 2018

Not everything that I think should go in is marked, but I went ahead and marked this :)

@charris
Copy link
Member
charris commented Apr 9, 2018

Also @pv wants your input on #10860.

@mattip mattip force-pushed the fix-PyArray_DeprecateWritebackIfCopy branch from 6faeb31 to d15c30f Compare April 21, 2018 20:55
@mattip
Copy link
Member Author
mattip commented Apr 21, 2018

Rebased off master to remove merge conflict

PyArray_ENABLEFLAGS(base, NPY_ARRAY_WRITEABLE);
PyArrayObject_fields *fa = (PyArrayObject_fields *)arr;
if (fa && fa->base) {
if ((fa->flags & NPY_ARRAY_UPDATEIFCOPY) || (fa->flags & NPY_ARRAY_WRITEBACKIFCOPY)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But now the line is too long :( Do it like

        if ((fa->flags & NPY_ARRAY_UPDATEIFCOPY) ||
                (fa->flags & NPY_ARRAY_WRITEBACKIFCOPY)) {
            PyArray_ENABLEFLAGS((PyArrayObject*)fa->base, NPY_ARRAY_WRITEABLE);

This is in the C style guide. Given that python style is now going to breaking before the operator, we might also want to do it like

        if ((fa->flags & NPY_ARRAY_UPDATEIFCOPY)
                || (fa->flags & NPY_ARRAY_WRITEBACKIFCOPY)) {
            PyArray_ENABLEFLAGS((PyArrayObject*)fa->base, NPY_ARRAY_WRITEABLE);

@charris
Copy link
Member
charris commented Apr 24, 2018

Just a style nit.

@charris
Copy link
Member
charris commented Apr 24, 2018

OK, I fixed it. Will put this in when the tests finish.

@charris
Copy link
Member
charris commented Apr 24, 2018

@ahaldane This would be good to backport as it fixes a bug in a function new to 1.14.

@charris charris added the 09 - Backport-Candidate PRs tagged should be backported label Apr 24, 2018
@charris charris merged commit ae940f9 into numpy:master Apr 25, 2018
@ahaldane
Copy link
Member

Will do

@mattip mattip deleted the fix-PyArray_DeprecateWritebackIfCopy branch May 6, 2018 13:30
@charris charris removed the 09 - Backport-Candidate PRs tagged should be backported label May 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0