Skip to content

Commit

Permalink
Fix reference counting of memoryview arguments to special functions
Browse files Browse the repository at this point in the history
Fixes cython#5571.

I believe this is to any function with a special signature
(essentially most dunder methods of cdef classes). The specific
report was about properties though.
  • Loading branch information
da-woods committed Jul 28, 2023
1 parent c8fe79d commit 3e9bb94
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 8 deletions.
17 changes: 9 additions & 8 deletions Cython/Compiler/Nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,9 @@ def generate_function_definitions(self, env, code):
have_gil=code.funcstate.gil_owned)
for entry in lenv.var_entries:
if entry.is_arg and entry.cf_is_reassigned and not entry.in_closure:
if entry.type.is_memoryviewslice:
code.put_var_incref_memoryviewslice(entry,
have_gil=code.funcstate.gil_owned)
if entry.xdecref_cleanup:
code.put_var_xincref(entry)
else:
Expand Down Expand Up @@ -2357,10 +2360,9 @@ def align_error_path_gil_to_success_path(): pass
if not entry.used or entry.in_closure:
continue

if entry.type.is_pyobject:
if entry.type.needs_refcounting:
if entry.is_arg and not entry.cf_is_reassigned:
continue
if entry.type.needs_refcounting:
assure_gil('success')
# FIXME ideally use entry.xdecref_cleanup but this currently isn't reliable
code.put_var_xdecref(entry, have_gil=gil_owned['success'])
Expand Down Expand Up @@ -3729,13 +3731,12 @@ def generate_function_definitions(self, env, code):

# ----- Non-error return cleanup
code.put_label(code.return_label)
for entry in lenv.var_entries:
if entry.is_arg:
# mainly captures the star/starstar args
if entry.xdecref_cleanup:
code.put_var_xdecref(entry)
for arg in [self.star_arg, self.starstar_arg]:
if arg and arg.entry.is_arg:
if arg.entry.xdecref_cleanup:
code.put_var_xdecref(arg.entry)
else:
code.put_var_decref(entry)
code.put_var_decref(arg.entry)
for arg in self.args:
if not arg.type.is_pyobject:
# This captures anything that's been converted from a PyObject.
Expand Down
40 changes: 40 additions & 0 deletions tests/memoryview/memoryview.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,46 @@ def test_acquire_memoryview_slice():
print b[2, 4]
print c[2, 4]

cdef class TestPassMemoryviewToSetter:
"""
Setter has a fixed function signature and the
argument needs conversion so it ends up passing through
some slightly different reference counting code
>>> dmb = DoubleMockBuffer("dmb", range(2), shape=(2,))
>>> TestPassMemoryviewToSetter().prop = dmb
acquired dmb
In prop setter
released dmb
>>> TestPassMemoryviewToSetter().prop_with_reassignment = dmb
acquired dmb
In prop_with_reassignment setter
released dmb
>>> dmb = DoubleMockBuffer("dmb", range(1,3), shape=(2,))
>>> TestPassMemoryviewToSetter().prop_with_reassignment = dmb
acquired dmb
In prop_with_reassignment setter
released dmb
"""
@property
def prop(self):
return None

@prop.setter
def prop(self, double[:] x):
print("In prop setter")

@property
def prop_with_reassignment(self):
return None

@prop_with_reassignment.setter
def prop_with_reassignment(self, double[:] x):
# reassignment again requires slightly different code
if x[0]:
x = x[1:]
print("In prop_with_reassignment setter")

class SingleObject(object):
def __init__(self, value):
self.value = value
Expand Down

0 comments on commit 3e9bb94

Please sign in to comment.