diff options
author | Stefan Behnel <stefan_ml@behnel.de> | 2014-04-26 18:49:48 +0200 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2014-04-26 18:49:48 +0200 |
commit | 68f9263017f4ea71940114d7a0669814ae15d01b (patch) | |
tree | 9dc724b4bd53d1aa30cb867d30f1030abfae6354 | |
parent | 58c7a8995b329f68a2a15d229fe0bac2a2b4cd4b (diff) | |
download | cython-68f9263017f4ea71940114d7a0669814ae15d01b.tar.gz |
fix memory leak for memory views in extension subtypes
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | Cython/Compiler/Symtab.py | 6 | ||||
-rw-r--r-- | Cython/Compiler/TypeSlots.py | 1 | ||||
-rw-r--r-- | tests/memoryview/memoryview_in_subclasses.pyx | 58 |
4 files changed, 68 insertions, 1 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index b05f9f93f..aea4b012d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,6 +15,10 @@ Features added Bugs fixed ---------- +* Memory leak when extension subtypes add a memory view as attribute + to those of the parent type without having Python object attributes + or a user provided dealloc method. + * Compiler crash on readonly properties in "binding" mode. * Auto-encoding with ``c_string_encoding=ascii`` failed in Py3.3. diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 37870cf57..8db1e6db7 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -1768,6 +1768,7 @@ class CClassScope(ClassScope): # method_table_cname string # getset_table_cname string # has_pyobject_attrs boolean Any PyObject attributes? + # has_memoryview_attrs boolean Any memory view attributes? # has_cyclic_pyobject_attrs boolean Any PyObject attributes that may need GC? # property_entries [Entry] # defined boolean Defined in .pxd file @@ -1777,6 +1778,7 @@ class CClassScope(ClassScope): is_c_class_scope = 1 has_pyobject_attrs = False + has_memoryview_attrs = False has_cyclic_pyobject_attrs = False defined = False implemented = False @@ -1850,7 +1852,9 @@ class CClassScope(ClassScope): entry = self.declare(name, cname, type, pos, visibility) entry.is_variable = 1 self.var_entries.append(entry) - if type.is_pyobject and name != '__weakref__': + if type.is_memoryviewslice: + self.has_memoryview_attrs = True + elif type.is_pyobject and name != '__weakref__': self.has_pyobject_attrs = True if (not type.is_builtin_type or not type.scope or type.scope.needs_gc()): diff --git a/Cython/Compiler/TypeSlots.py b/Cython/Compiler/TypeSlots.py index 6595cf514..176160226 100644 --- a/Cython/Compiler/TypeSlots.py +++ b/Cython/Compiler/TypeSlots.py @@ -353,6 +353,7 @@ class ConstructorSlot(InternalMethodSlot): if (self.slot_name != 'tp_new' and scope.parent_type.base_type and not scope.has_pyobject_attrs + and not scope.has_memoryview_attrs and not scope.lookup_here(self.method)): # if the type does not have object attributes, it can # delegate GC methods to its parent - iff the parent diff --git a/tests/memoryview/memoryview_in_subclasses.pyx b/tests/memoryview/memoryview_in_subclasses.pyx new file mode 100644 index 000000000..900cddb4a --- /dev/null +++ b/tests/memoryview/memoryview_in_subclasses.pyx @@ -0,0 +1,58 @@ +""" +Test for memory leaks when adding more memory view attributes in subtypes. +""" + +import gc + +from cython.view cimport array + + +def count_memoryviews(): + gc.collect() + return sum([1 if 'memoryview' in str(type(o)) else 0 + for o in gc.get_objects()]) + + +def run_test(cls, num_iters): + orig_count = count_memoryviews() + def f(): + x = cls(1024) + for i in range(num_iters): + f() + return count_memoryviews() - orig_count + + +cdef class BaseType: + """ + >>> run_test(BaseType, 10) + 0 + """ + cdef double[:] buffer + + def __cinit__(self, n): + self.buffer = array((n,), sizeof(double), 'd') + + +cdef class Subtype(BaseType): + """ + >>> run_test(Subtype, 10) + 0 + """ + cdef double[:] buffer2 + + def __cinit__(self, n): + self.buffer2 = array((n,), sizeof(double), 'd') + + +cdef class SubtypeWithUserDealloc(BaseType): + """ + >>> run_test(SubtypeWithUserDealloc, 10) + 0 + """ + cdef double[:] buffer2 + + def __cinit__(self, n): + self.buffer2 = array((n,), sizeof(double), 'd') + + def __dealloc__(self): + pass |