[pypy-commit] pypy gc-del: in-progress

arigo noreply at buildbot.pypy.org
Mon Mar 4 10:04:46 CET 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: gc-del
Changeset: r61993:ac5c5480c694
Date: 2013-03-04 10:04 +0100
http://bitbucket.org/pypy/pypy/changeset/ac5c5480c694/

Log:	in-progress

diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -11,7 +11,7 @@
      WEAKREFPTR
 from rpython.tool.sourcetools import func_with_new_name
 from rpython.translator.backendopt import graphanalyze
-from rpython.translator.backendopt.finalizer import FinalizerAnalyzer
+from rpython.translator.backendopt.destructor import DestructorAnalyzer
 from rpython.translator.backendopt.support import var_needsgc
 import types
 
@@ -650,21 +650,15 @@
         info = self.layoutbuilder.get_info(type_id)
         c_size = rmodel.inputconst(lltype.Signed, info.fixedsize)
         kind_and_fptr = self.special_funcptr_for_type(TYPE)
-        has_finalizer = (kind_and_fptr is not None and
-                         kind_and_fptr[0] == "finalizer")
-        has_light_finalizer = (kind_and_fptr is not None and
-                               kind_and_fptr[0] == "light_finalizer")
-        if has_light_finalizer:
-            has_finalizer = True
-        c_has_finalizer = rmodel.inputconst(lltype.Bool, has_finalizer)
-        c_has_light_finalizer = rmodel.inputconst(lltype.Bool,
-                                                  has_light_finalizer)
+        has_destructor = (kind_and_fptr is not None and
+                          kind_and_fptr[0] == "destructor")
+        c_has_destructor = rmodel.inputconst(lltype.Bool, has_destructor)
 
         if not op.opname.endswith('_varsize') and not flags.get('varsize'):
             #malloc_ptr = self.malloc_fixedsize_ptr
             zero = flags.get('zero', False)
             if (self.malloc_fast_ptr is not None and
-                not c_has_finalizer.value and
+                not has_destructor and
                 (self.malloc_fast_is_clearing or not zero)):
                 malloc_ptr = self.malloc_fast_ptr
             elif zero:
@@ -672,10 +666,9 @@
             else:
                 malloc_ptr = self.malloc_fixedsize_ptr
             args = [self.c_const_gc, c_type_id, c_size,
-                    c_has_finalizer, c_has_light_finalizer,
-                    rmodel.inputconst(lltype.Bool, False)]
+                    c_has_destructor, rmodel.inputconst(lltype.Bool, False)]
         else:
-            assert not c_has_finalizer.value
+            assert not has_destructor
             info_varsize = self.layoutbuilder.get_info_varsize(type_id)
             v_length = op.args[-1]
             c_ofstolength = rmodel.inputconst(lltype.Signed,
@@ -830,13 +823,12 @@
     def gct_do_malloc_fixedsize_clear(self, hop):
         # used by the JIT (see rpython.jit.backend.llsupport.gc)
         op = hop.spaceop
-        [v_typeid, v_size,
-         v_has_finalizer, v_has_light_finalizer, v_contains_weakptr] = op.args
+        [v_typeid, v_size, v_has_destructor, v_contains_weakptr] = op.args
         livevars = self.push_roots(hop)
         hop.genop("direct_call",
                   [self.malloc_fixedsize_clear_ptr, self.c_const_gc,
                    v_typeid, v_size,
-                   v_has_finalizer, v_has_light_finalizer,
+                   v_has_destructor,
                    v_contains_weakptr],
                   resultvar=op.result)
         self.pop_roots(hop, livevars)
@@ -1204,6 +1196,9 @@
     def pop_roots(self, hop, livevars):
         raise NotImplementedError
 
+    def gct_gc_register_finalizer(self, op):
+        xxx
+
 
 class TransformerLayoutBuilder(gctypelayout.TypeLayoutBuilder):
 
@@ -1218,36 +1213,32 @@
         self.translator = translator
         super(TransformerLayoutBuilder, self).__init__(GCClass, lltype2vtable)
 
-    def has_finalizer(self, TYPE):
+    def has_destructor(self, TYPE):
         rtti = get_rtti(TYPE)
         return rtti is not None and getattr(rtti._obj, 'destructor_funcptr',
                                             None)
 
-    def has_light_finalizer(self, TYPE):
-        special = self.special_funcptr_for_type(TYPE)
-        return special is not None and special[0] == 'light_finalizer'
-
     def has_custom_trace(self, TYPE):
         rtti = get_rtti(TYPE)
         return rtti is not None and getattr(rtti._obj, 'custom_trace_funcptr',
                                             None)
 
-    def make_finalizer_funcptr_for_type(self, TYPE):
-        if not self.has_finalizer(TYPE):
-            return None, False
+    def make_destructor_funcptr_for_type(self, TYPE):
+        if not self.has_destructor(TYPE):
+            return None
         rtti = get_rtti(TYPE)
         destrptr = rtti._obj.destructor_funcptr
         DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
         typename = TYPE.__name__
-        def ll_finalizer(addr, ignored):
+        def ll_destructor(addr, ignored):
             v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG)
             ll_call_destructor(destrptr, v, typename)
             return llmemory.NULL
-        fptr = self.transformer.annotate_finalizer(ll_finalizer,
+        fptr = self.transformer.annotate_finalizer(ll_destructor,
                 [llmemory.Address, llmemory.Address], llmemory.Address)
         g = destrptr._obj.graph
-        light = not FinalizerAnalyzer(self.translator).analyze_light_finalizer(g)
-        return fptr, light
+        DestructorAnalyzer(self.translator).check_destructor(g)
+        return fptr
 
     def make_custom_trace_funcptr_for_type(self, TYPE):
         if not self.has_custom_trace(TYPE):
diff --git a/rpython/memory/gctypelayout.py b/rpython/memory/gctypelayout.py
--- a/rpython/memory/gctypelayout.py
+++ b/rpython/memory/gctypelayout.py
@@ -377,15 +377,12 @@
     def special_funcptr_for_type(self, TYPE):
         if TYPE in self._special_funcptrs:
             return self._special_funcptrs[TYPE]
-        fptr1, is_lightweight = self.make_finalizer_funcptr_for_type(TYPE)
+        fptr1 = self.make_destructor_funcptr_for_type(TYPE)
         fptr2 = self.make_custom_trace_funcptr_for_type(TYPE)
         assert not (fptr1 and fptr2), (
             "type %r needs both a finalizer and a custom tracer" % (TYPE,))
         if fptr1:
-            if is_lightweight:
-                kind_and_fptr = "light_finalizer", fptr1
-            else:
-                kind_and_fptr = "finalizer", fptr1
+            kind_and_fptr = "destructor", fptr1
         elif fptr2:
             kind_and_fptr = "custom_trace", fptr2
         else:
@@ -393,9 +390,9 @@
         self._special_funcptrs[TYPE] = kind_and_fptr
         return kind_and_fptr
 
-    def make_finalizer_funcptr_for_type(self, TYPE):
+    def make_destructor_funcptr_for_type(self, TYPE):
         # must be overridden for proper finalizer support
-        return None, False
+        return None
 
     def make_custom_trace_funcptr_for_type(self, TYPE):
         # must be overridden for proper custom tracer support
diff --git a/rpython/memory/gcwrapper.py b/rpython/memory/gcwrapper.py
--- a/rpython/memory/gcwrapper.py
+++ b/rpython/memory/gcwrapper.py
@@ -1,4 +1,4 @@
-from rpython.translator.backendopt.finalizer import FinalizerAnalyzer
+from rpython.translator.backendopt.destructor import DestructorAnalyzer
 from rpython.rtyper.lltypesystem import lltype, llmemory, llheap
 from rpython.rtyper import llinterp
 from rpython.rtyper.annlowlevel import llhelper
@@ -204,7 +204,7 @@
         self.llinterp = llinterp
         super(DirectRunLayoutBuilder, self).__init__(GCClass, lltype2vtable)
 
-    def make_finalizer_funcptr_for_type(self, TYPE):
+    def make_destructor_funcptr_for_type(self, TYPE):
         from rpython.memory.gctransform.support import get_rtti
         rtti = get_rtti(TYPE)
         if rtti is not None and hasattr(rtti._obj, 'destructor_funcptr'):
@@ -212,10 +212,10 @@
             DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
             destrgraph = destrptr._obj.graph
         else:
-            return None, False
+            return None
 
         t = self.llinterp.typer.annotator.translator
-        light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
+        DestructorAnalyzer(t).check_destructor(destrgraph)
         def ll_finalizer(addr, dummy):
             assert dummy == llmemory.NULL
             try:
@@ -225,7 +225,7 @@
                 raise RuntimeError(
                     "a finalizer raised an exception, shouldn't happen")
             return llmemory.NULL
-        return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer), light
+        return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer)
 
     def make_custom_trace_funcptr_for_type(self, TYPE):
         from rpython.memory.gctransform.support import get_rtti
diff --git a/rpython/memory/test/snippet.py b/rpython/memory/test/snippet.py
--- a/rpython/memory/test/snippet.py
+++ b/rpython/memory/test/snippet.py
@@ -2,6 +2,7 @@
 from rpython.tool.udir import udir
 from rpython.rtyper.lltypesystem import lltype
 from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rlib import rgc
 
 
 class SemiSpaceGCTestDefines:
@@ -51,7 +52,8 @@
             def __init__(self, key):
                 self.key = key
                 self.refs = []
-            def __del__(self):
+                rgc.register_finalizer(self.finalize)
+            def finalize(self):
                 assert state.age[self.key] == -1
                 state.age[self.key] = state.time
                 state.progress = True
@@ -113,8 +115,8 @@
 
         return f
 
-    def test_finalizer_order(self):
-        res = self.run('finalizer_order')
+    def test_full_finalizer_order(self):
+        res = self.run('full_finalizer_order')
         if res != "ok":
             i, summary, msg = res.split('\n')
             i = int(i)
diff --git a/rpython/memory/test/test_gc.py b/rpython/memory/test/test_gc.py
--- a/rpython/memory/test/test_gc.py
+++ b/rpython/memory/test/test_gc.py
@@ -129,7 +129,7 @@
         assert res == concat(100)
         #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
 
-    def test_finalizer(self):
+    def test_destructor(self):
         class B(object):
             pass
         b = B()
@@ -153,88 +153,94 @@
         res = self.interpret(f, [5])
         assert res == 6
 
-    def test_finalizer_calls_malloc(self):
+    def test_finalizer(self):
         class B(object):
             pass
         b = B()
         b.nextid = 0
+        b.num_finalized = 0
+        class A(object):
+            def __init__(self):
+                self.id = b.nextid
+                b.nextid += 1
+            def finalizer(self):
+                b.num_finalized += 1
+        def allocate(x):
+            i = 0
+            while i < x:
+                i += 1
+                a = A()
+                rgc.register_finalizer(a.finalizer)
+        def f(x):
+            allocate(x)
+            llop.gc__collect(lltype.Void)
+            llop.gc__collect(lltype.Void)
+            return b.num_finalized
+        res = self.interpret(f, [6])
+        assert res == 6
+
+    def test_finalizer_and_destructor(self):
+        class B(object):
+            pass
+        b = B()
+        b.nextid = 0
+        b.num_finalized = 0
         b.num_deleted = 0
         class A(object):
             def __init__(self):
                 self.id = b.nextid
                 b.nextid += 1
-            def __del__(self):
-                b.num_deleted += 1
-                C()
-        class C(A):
+            def finalizer(self):
+                assert n.num_deleted <= b.num_finalized
+                b.num_finalized += 1
             def __del__(self):
                 b.num_deleted += 1
         def f(x):
             a = A()
+            rgc.register_finalizer(a.finalizer)
             i = 0
             while i < x:
                 i += 1
                 a = A()
             llop.gc__collect(lltype.Void)
             llop.gc__collect(lltype.Void)
-            return b.num_deleted
+            return b.num_finalized * 100 + b.num_deleted
         res = self.interpret(f, [5])
-        assert res == 12
+        assert res == 606
 
-    def test_finalizer_calls_collect(self):
+    def test_finalize_later(self):
         class B(object):
             pass
         b = B()
         b.nextid = 0
-        b.num_deleted = 0
+        b.num_finalized = 0
         class A(object):
             def __init__(self):
                 self.id = b.nextid
                 b.nextid += 1
-            def __del__(self):
-                b.num_deleted += 1
-                llop.gc__collect(lltype.Void)
+            def finalizer(self):
+                b.num_finalized += 1
+                if (b.num_finalized % 3) == 0:
+                    raise rgc.FinalizeLater
         def f(x):
             a = A()
+            rgc.register_finalizer(a.finalizer)
             i = 0
             while i < x:
                 i += 1
                 a = A()
             llop.gc__collect(lltype.Void)
-            llop.gc__collect(lltype.Void)
-            return b.num_deleted
+            if b.num_finalized == 0:
+                llop.gc__collect(lltype.Void)
+            assert b.num_finalized == 3
+            rgc.progress_through_finalizer_queue()
+            assert b.num_finalized == 6
+            rgc.progress_through_finalizer_queue()
+            assert b.num_finalized == 8
+            rgc.progress_through_finalizer_queue()
+            assert b.num_finalized == 8
         res = self.interpret(f, [5])
-        assert res == 6
-
-    def test_finalizer_resurrects(self):
-        class B(object):
-            pass
-        b = B()
-        b.nextid = 0
-        b.num_deleted = 0
-        class A(object):
-            def __init__(self):
-                self.id = b.nextid
-                b.nextid += 1
-            def __del__(self):
-                b.num_deleted += 1
-                b.a = self
-        def f(x):
-            a = A()
-            i = 0
-            while i < x:
-                i += 1
-                a = A()
-            llop.gc__collect(lltype.Void)
-            llop.gc__collect(lltype.Void)
-            aid = b.a.id
-            b.a = None
-            # check that __del__ is not called again
-            llop.gc__collect(lltype.Void)
-            llop.gc__collect(lltype.Void)
-            return b.num_deleted * 10 + aid + 100 * (b.a is None)
-        res = self.interpret(f, [5])
-        assert 160 <= res <= 165
+        assert res == 606
 
     def test_custom_trace(self):
         from rpython.rtyper.annlowlevel import llhelper
diff --git a/rpython/translator/backendopt/finalizer.py b/rpython/translator/backendopt/destructor.py
rename from rpython/translator/backendopt/finalizer.py
rename to rpython/translator/backendopt/destructor.py
--- a/rpython/translator/backendopt/finalizer.py
+++ b/rpython/translator/backendopt/destructor.py
@@ -2,15 +2,12 @@
 from rpython.translator.backendopt import graphanalyze
 from rpython.rtyper.lltypesystem import lltype
 
-class FinalizerError(Exception):
-    """ __del__ marked as lightweight finalizer, but the analyzer did
-    not agree
-    """
+class DestructorError(Exception):
+    """The __del__() method contains unsupported operations"""
 
-class FinalizerAnalyzer(graphanalyze.BoolGraphAnalyzer):
-    """ Analyzer that determines whether a finalizer is lightweight enough
-    so it can be called without all the complicated logic in the garbage
-    collector. The set of operations here is restrictive for a good reason
+class DestructorAnalyzer(graphanalyze.BoolGraphAnalyzer):
+    """ Analyzer that checks if a destructor is lightweight enough for
+    RPython.  The set of operations here is restrictive for a good reason
     - it's better to be safe. Specifically disallowed operations:
 
     * anything that escapes self
@@ -20,12 +17,10 @@
                      'direct_ptradd', 'force_cast', 'track_alloc_stop',
                      'raw_free']
 
-    def analyze_light_finalizer(self, graph):
+    def check_destructor(self, graph):
         result = self.analyze_direct_call(graph)
-        if (result is self.top_result() and
-            getattr(graph.func, '_must_be_light_finalizer_', False)):
-            raise FinalizerError(FinalizerError.__doc__, graph)
-        return result
+        if result is self.top_result():
+            raise DestructorError(DestructorError.__doc__, graph)
 
     def analyze_simple_operation(self, op, graphinfo):
         if op.opname in self.ok_operations:


More information about the pypy-commit mailing list