[pypy-commit] pypy lightweight-finalizers: Make rclass aware of lightweight finalizers. Write a test in test_gc and

fijal noreply at buildbot.pypy.org
Thu Sep 29 22:34:23 CEST 2011


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: lightweight-finalizers
Changeset: r47692:d7d83ce34d5d
Date: 2011-09-29 17:34 -0300
http://bitbucket.org/pypy/pypy/changeset/d7d83ce34d5d/

Log:	Make rclass aware of lightweight finalizers. Write a test in test_gc
	and make it pass (badly?) on marksweep

diff --git a/pypy/rpython/lltypesystem/lltype.py b/pypy/rpython/lltypesystem/lltype.py
--- a/pypy/rpython/lltypesystem/lltype.py
+++ b/pypy/rpython/lltypesystem/lltype.py
@@ -363,7 +363,7 @@
         Struct._install_extras(self, **kwds)
 
     def _attach_runtime_type_info_funcptr(self, funcptr, destrptr,
-                                          customtraceptr):
+                                          customtraceptr, raw_mem_attr_name):
         if self._runtime_type_info is None:
             raise TypeError("attachRuntimeTypeInfo: %r must have been built "
                             "with the rtti=True argument" % (self,))
@@ -399,6 +399,8 @@
                 raise TypeError("expected a custom trace function "
                                 "implementation, got: %s" % customtraceptr)
             self._runtime_type_info.custom_trace_funcptr = customtraceptr
+        if raw_mem_attr_name is not None:
+            self._runtime_type_info.raw_mem_attr_name = raw_mem_attr_name
 
 class GcStruct(RttiStruct):
     _gckind = 'gc'
@@ -2058,11 +2060,12 @@
     return _ptr(PTRTYPE, oddint, solid=True)
 
 def attachRuntimeTypeInfo(GCSTRUCT, funcptr=None, destrptr=None,
-                          customtraceptr=None):
+                          customtraceptr=None, raw_mem_attr_name=None):
     if not isinstance(GCSTRUCT, RttiStruct):
         raise TypeError, "expected a RttiStruct: %s" % GCSTRUCT
     GCSTRUCT._attach_runtime_type_info_funcptr(funcptr, destrptr,
-                                               customtraceptr)
+                                               customtraceptr,
+                                               raw_mem_attr_name)
     return _ptr(Ptr(RuntimeTypeInfo), GCSTRUCT._runtime_type_info)
 
 def getRuntimeTypeInfo(GCSTRUCT):
diff --git a/pypy/rpython/lltypesystem/rclass.py b/pypy/rpython/lltypesystem/rclass.py
--- a/pypy/rpython/lltypesystem/rclass.py
+++ b/pypy/rpython/lltypesystem/rclass.py
@@ -391,26 +391,39 @@
     def _setup_repr_final(self):
         AbstractInstanceRepr._setup_repr_final(self)
         if self.gcflavor == 'gc':
-            if (self.classdef is not None and
-                self.classdef.classdesc.lookup('__del__') is not None):
-                s_func = self.classdef.classdesc.s_read_attribute('__del__')
-                source_desc = self.classdef.classdesc.lookup('__del__')
-                source_classdef = source_desc.getclassdef(None)
-                source_repr = getinstancerepr(self.rtyper, source_classdef)
-                assert len(s_func.descriptions) == 1
-                funcdesc, = s_func.descriptions
-                graph = funcdesc.getuniquegraph()
-                self.check_graph_of_del_does_not_call_too_much(graph)
-                FUNCTYPE = FuncType([Ptr(source_repr.object_type)], Void)
-                destrptr = functionptr(FUNCTYPE, graph.name,
-                                       graph=graph,
-                                       _callable=graph.func)
-            else:
-                destrptr = None
+            destrptr = None
+            raw_mem_attr_name = None
+            if self.classdef is not None:
+                classdesc = self.classdef.classdesc
+                if classdesc.lookup('__del__') is not None:
+                    s_func = classdesc.s_read_attribute('__del__')
+                    if classdesc.lookup('_raw_mem_ptr_name'):
+                        # this object has a __del__, but it actually does not
+                        # do much
+                        assert s_func.const.func_name == 'remove_raw_mem_attr',(
+                            "You overloaded __del__ on an object that owns"
+                            " a reference to a low level pointer")
+                        raw_mem_attr_name = classdesc.s_read_attribute(
+                            '_raw_mem_ptr_name').const
+                    else:
+                        source_desc = classdesc.lookup('__del__')
+                        source_classdef = source_desc.getclassdef(None)
+                        source_repr = getinstancerepr(self.rtyper,
+                                                      source_classdef)
+                        assert len(s_func.descriptions) == 1
+                        funcdesc, = s_func.descriptions
+                        graph = funcdesc.getuniquegraph()
+                        self.check_graph_of_del_does_not_call_too_much(graph)
+                        FUNCTYPE = FuncType([Ptr(source_repr.object_type)],
+                                            Void)
+                        destrptr = functionptr(FUNCTYPE, graph.name,
+                                               graph=graph,
+                                               _callable=graph.func)
             OBJECT = OBJECT_BY_FLAVOR[LLFLAVOR[self.gcflavor]]
             self.rtyper.attachRuntimeTypeInfoFunc(self.object_type,
                                                   ll_runtime_type_info,
-                                                  OBJECT, destrptr)
+                                                  OBJECT, destrptr,
+                                                  raw_mem_attr_name)
             vtable = self.rclass.getvtable()
             self.rtyper.set_type_for_typeptr(vtable, self.lowleveltype.TO)
 
diff --git a/pypy/rpython/memory/gc/base.py b/pypy/rpython/memory/gc/base.py
--- a/pypy/rpython/memory/gc/base.py
+++ b/pypy/rpython/memory/gc/base.py
@@ -72,7 +72,9 @@
                             is_rpython_class,
                             has_custom_trace,
                             get_custom_trace,
-                            fast_path_tracing):
+                            fast_path_tracing,
+                            has_raw_mem_ptr,
+                            ofs_to_raw_mem_ptr):
         self.getfinalizer = getfinalizer
         self.is_varsize = is_varsize
         self.has_gcptr_in_varsize = has_gcptr_in_varsize
@@ -89,6 +91,8 @@
         self.has_custom_trace = has_custom_trace
         self.get_custom_trace = get_custom_trace
         self.fast_path_tracing = fast_path_tracing
+        self.has_raw_mem_ptr = has_raw_mem_ptr
+        self.ofs_to_raw_mem_ptr = ofs_to_raw_mem_ptr
 
     def get_member_index(self, type_id):
         return self.member_index(type_id)
diff --git a/pypy/rpython/memory/gc/marksweep.py b/pypy/rpython/memory/gc/marksweep.py
--- a/pypy/rpython/memory/gc/marksweep.py
+++ b/pypy/rpython/memory/gc/marksweep.py
@@ -362,6 +362,11 @@
                     obj = gc_info + size_gc_header
                     self.write_free_statistics(typeid, obj)
                     freed_size += estimate
+                    if self.has_raw_mem_ptr(typeid):
+                        p = (addr + size_gc_header +
+                             self.ofs_to_raw_mem_ptr(typeid)).ptr[0]
+                        if p:
+                            lltype.free(p, flavor='raw')
                     raw_free(addr)
                 hdr = next
             ppnext.address[0] = llmemory.NULL
diff --git a/pypy/rpython/memory/gctypelayout.py b/pypy/rpython/memory/gctypelayout.py
--- a/pypy/rpython/memory/gctypelayout.py
+++ b/pypy/rpython/memory/gctypelayout.py
@@ -34,6 +34,8 @@
         ("finalizer_or_customtrace", FINALIZER_OR_CT),
         ("fixedsize",      lltype.Signed),
         ("ofstoptrs",      lltype.Ptr(OFFSETS_TO_GC_PTR)),
+        ("ofstorawptr",    lltype.Signed),
+        # XXX merge me with finalizer_or_custometrace
         hints={'immutable': True},
         )
     VARSIZE_TYPE_INFO = lltype.Struct("varsize_type_info",
@@ -136,6 +138,12 @@
         infobits = self.get(typeid).infobits
         return infobits & T_ANY_SLOW_FLAG == 0
 
+    def q_has_raw_mem_ptr(self, typeid):
+        return self.get(typeid).infobits & T_HAS_RAW_MEM_PTR != 0
+
+    def q_ofs_to_raw_mem_ptr(self, typeid):
+        return self.get(typeid).ofstorawptr
+
     def set_query_functions(self, gc):
         gc.set_query_functions(
             self.q_is_varsize,
@@ -153,7 +161,9 @@
             self.q_is_rpython_class,
             self.q_has_custom_trace,
             self.q_get_custom_trace,
-            self.q_fast_path_tracing)
+            self.q_fast_path_tracing,
+            self.q_has_raw_mem_ptr,
+            self.q_ofs_to_raw_mem_ptr)
 
 
 # the lowest 16bits are used to store group member index
@@ -165,6 +175,7 @@
 T_IS_RPYTHON_INSTANCE  = 0x100000    # the type is a subclass of OBJECT
 T_HAS_FINALIZER        = 0x200000
 T_HAS_CUSTOM_TRACE     = 0x400000
+T_HAS_RAW_MEM_PTR      = 0x800000
 T_KEY_MASK             = intmask(0xFF000000)
 T_KEY_VALUE            = intmask(0x5A000000)    # bug detection only
 
@@ -236,6 +247,10 @@
         infobits |= T_IS_WEAKREF
     if is_subclass_of_object(TYPE):
         infobits |= T_IS_RPYTHON_INSTANCE
+    if getattr(TYPE._runtime_type_info, 'raw_mem_attr_name', None):
+        name = TYPE._runtime_type_info.raw_mem_attr_name
+        infobits |= T_HAS_RAW_MEM_PTR
+        info.ofstorawptr = llmemory.offsetof(TYPE, 'inst_' + name)
     info.infobits = infobits | T_KEY_VALUE
 
 # ____________________________________________________________
diff --git a/pypy/rpython/memory/test/test_gc.py b/pypy/rpython/memory/test/test_gc.py
--- a/pypy/rpython/memory/test/test_gc.py
+++ b/pypy/rpython/memory/test/test_gc.py
@@ -129,6 +129,27 @@
         res = self.interpret(concat, [100])
         assert res == concat(100)
         #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
+
+    def test_lightweight_finalizer(self):
+        T = lltype.Struct('T', ('x', lltype.Signed))
+        
+        @rgc.owns_raw_memory('p')
+        class AClass(object):
+            p = lltype.nullptr(T)
+
+            def __init__(self, arg):
+                if arg:
+                    self.p = lltype.malloc(T, flavor='raw')
+
+        def f():
+            for i in range(3):
+                AClass(3)
+                AClass(0)
+            llop.gc__collect(lltype.Void)
+            # assert did not crash with malloc mismatch, ie those things
+            # has been freed
+
+        self.interpret(f, [])
         
     def test_finalizer(self):
         class B(object):
diff --git a/pypy/rpython/rtyper.py b/pypy/rpython/rtyper.py
--- a/pypy/rpython/rtyper.py
+++ b/pypy/rpython/rtyper.py
@@ -705,7 +705,7 @@
         return self.getcallable(graph)
 
     def attachRuntimeTypeInfoFunc(self, GCSTRUCT, func, ARG_GCSTRUCT=None,
-                                  destrptr=None):
+                                  destrptr=None, raw_mem_attr_name=None):
         self.call_all_setups()  # compute ForwardReferences now
         if ARG_GCSTRUCT is None:
             ARG_GCSTRUCT = GCSTRUCT
@@ -717,7 +717,8 @@
             raise TyperError("runtime type info function %r returns %r, "
                              "excepted Ptr(RuntimeTypeInfo)" % (func, s))
         funcptr = self.getcallable(graph)
-        attachRuntimeTypeInfo(GCSTRUCT, funcptr, destrptr)
+        attachRuntimeTypeInfo(GCSTRUCT, funcptr, destrptr, None,
+                              raw_mem_attr_name)
 
 # register operations from annotation model
 RPythonTyper._registeroperations(annmodel)
diff --git a/pypy/rpython/test/test_rclass.py b/pypy/rpython/test/test_rclass.py
--- a/pypy/rpython/test/test_rclass.py
+++ b/pypy/rpython/test/test_rclass.py
@@ -4,6 +4,7 @@
 from pypy.rpython.lltypesystem.lltype import *
 from pypy.rpython.ootypesystem import ootype
 from pypy.rlib.rarithmetic import intmask, r_longlong
+from pypy.rlib import rgc
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
 from pypy.rpython.rclass import IR_IMMUTABLE, IR_IMMUTABLE_ARRAY
 from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
@@ -972,9 +973,31 @@
         graph = graphof(t, f)
         TYPE = graph.startblock.operations[0].args[0].value
         RTTI = getRuntimeTypeInfo(TYPE)
-        queryptr = RTTI._obj.query_funcptr # should not raise
+        RTTI._obj.query_funcptr # should not raise
         destrptr = RTTI._obj.destructor_funcptr
         assert destrptr is not None
+
+    def test_lightweight_del(self):
+        T = Struct('T', ('x', Signed))
+        
+        @rgc.owns_raw_memory('p')
+        class A(object):
+            p = nullptr(T)
+
+            def __init__(self, arg):
+                self.p = malloc(T, flavor='raw')
+
+        def f():
+            A(3)
+
+        t = TranslationContext()
+        t.buildannotator().build_types(f, [])
+        t.buildrtyper().specialize()
+        graph = graphof(t, f)
+        TYPE = graph.startblock.operations[0].args[0].value
+        RTTI = getRuntimeTypeInfo(TYPE)
+        RTTI._obj.query_funcptr # should not raise
+        assert RTTI._obj.raw_mem_attr_name == 'p'
     
     def test_del_inheritance(self):
         from pypy.rlib import rgc


More information about the pypy-commit mailing list