[Cython] memoryview slice transpose
Syam Gadde
syam.gadde at duke.edu
Mon Apr 7 21:38:49 CEST 2014
My patch actually addressed two slightly different issues, both of which
have to do with refcounts of transposed slices. I'll address the first
one below since that's the one that's not so obvious.
Here's the code that causes an "Acquisition count is 0" error:
import numpy
cimport numpy
def testfunc():
a = numpy.arange(12).reshape([3,4])
cdef numpy.int_t[:,:] a_view = a
cdef numpy.int_t[:,:] b = a_view[:,:].T
raise Exception() # comment this out and you still get a refcount
error at a different line
testfunc()
On 04/06/2014 06:12 AM, Stefan Behnel wrote:
> Syam Gadde, 27.03.2014 21:29:
>> One problem is that when a memoryview slice is decremented, the .memview
>> and .data fields are not set to NULL, and any error that causes a jump
>> to cleanup code will attempt to decref it again.
> Hmm, I can't see why the additional NULL setting would change anything.
> That's what the call to put_xdecref_memoryviewslice() already did that
> immediately precedes them (it actually calls "__Pyx_XDEC_MEMVIEW()" in
> MemoryView_C.c).
But only if the refcount of the memview goes to zero.Maybe that's what
needs to change?
See the case below (look for the *****'s). memviewslice objects are
defined at the top of the function and on the stack, so they persist
throughout the function. Several memviewslice objects may share the
same base memview object. So if a memviewslice object finished doing
what it needs to do, it needs to decrement the refcount of the memview
object it's pointing to using __PYX_XDEC_MEMVIEW, *and* detach itself
from the memview (that's the setting .memview = NULL part). The reason
the explicit detaching is important is that there are extra
__PYX_XDEC_MEMVIEW() calls in the error cleanup code, presumably to
decrement the reference counts for still attached memviews in the case
there is an error/exception. If the memview field is set to NULL
whenever a slice is done with it, the extra calls to __PYX_XDEC_MEMVIEW
are OK because it checks to see whether .memview is NULL before going
forward. But currently, if an error/exception occurs while the slice is
still attached to the memview, you can end up with a refcount < 0.
If __PYX_XDEC_MEMVIEW did indeed set .memview to NULL on every call,
then this wouldn't be necessary. But I have not tried that to see if it
breaks something else.
/* "testtranspose.pyx":7
* a = numpy.arange(12).reshape([3,4])
* cdef numpy.int_t[:,:] a_view = a
* cdef numpy.int_t[:,:] b = a_view[:,:].T # <<<<<<<<<<<<<<
* raise Exception()
*
*/
__pyx_t_6 = -1;
__pyx_t_5.data = __pyx_v_a_view.data;
__pyx_t_5.memview = __pyx_v_a_view.memview;
__PYX_INC_MEMVIEW(&__pyx_t_5, 0);
__pyx_t_5.shape[0] = __pyx_v_a_view.shape[0];
__pyx_t_5.strides[0] = __pyx_v_a_view.strides[0];
__pyx_t_5.suboffsets[0] = -1;
__pyx_t_5.shape[1] = __pyx_v_a_view.shape[1];
__pyx_t_5.strides[1] = __pyx_v_a_view.strides[1];
__pyx_t_5.suboffsets[1] = -1;
__pyx_t_7 = __pyx_t_5;
if (unlikely(__pyx_memslice_transpose(&__pyx_t_7) == 0))
{__pyx_filename = __p
yx_f[0]; __pyx_lineno = 7; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
__PYX_XDEC_MEMVIEW(&__pyx_t_5, 1);//**************************
__pyx_v_b = __pyx_t_7;
__pyx_t_7.memview = NULL;
__pyx_t_7.data = NULL;
/* "testtranspose.pyx":8
* cdef numpy.int_t[:,:] a_view = a
* cdef numpy.int_t[:,:] b = a_view[:,:].T
* raise Exception() # <<<<<<<<<<<<<<
*
* testfunc()
*/
__pyx_t_1 = __Pyx_PyObject_Call(__pyx_builtin_Exception,
__pyx_empty_tuple, NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename =
__pyx_f[0]; __pyx_lineno = 8; __pyx_clineno = __LINE__; goto
__pyx_L1_error;}
__Pyx_GOTREF(__pyx_t_1);
__Pyx_Raise(__pyx_t_1, 0, 0, 0);
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
{__pyx_filename = __pyx_f[0]; __pyx_lineno = 8; __pyx_clineno =
__LINE__; goto __pyx_L1_error;}
/* "testtranspose.pyx":4
* cimport numpy
*
* def testfunc(): # <<<<<<<<<<<<<<
* a = numpy.arange(12).reshape([3,4])
* cdef numpy.int_t[:,:] a_view = a
*/
/* function exit code */
__pyx_L1_error:; // ****** exception causes a jump to here *****
__Pyx_XDECREF(__pyx_t_1);
__Pyx_XDECREF(__pyx_t_2);
__Pyx_XDECREF(__pyx_t_3);
__PYX_XDEC_MEMVIEW(&__pyx_t_4, 1);
__PYX_XDEC_MEMVIEW(&__pyx_t_5, 1); //****************************
__PYX_XDEC_MEMVIEW(&__pyx_t_7, 1);
__Pyx_AddTraceback("testtranspose.testfunc", __pyx_clineno,
__pyx_lineno, __pyx_filename);
__pyx_r = NULL;
__Pyx_XDECREF(__pyx_v_a);
__PYX_XDEC_MEMVIEW(&__pyx_v_a_view, 1);
__PYX_XDEC_MEMVIEW(&__pyx_v_b, 1);
__Pyx_XGIVEREF(__pyx_r);
__Pyx_RefNannyFinishContext();
return __pyx_r;
More information about the cython-devel
mailing list