Author: Armin Rigo <[email protected]>
Branch: gc-del-3
Changeset: r84187:6b9a7ecbd6ad
Date: 2016-05-04 17:02 +0200
http://bitbucket.org/pypy/pypy/changeset/6b9a7ecbd6ad/

Log:    Found the best way forward: restore much of the removed support for
        non-light __del__ and keep both finalizer solutions for now

diff --git a/rpython/memory/gc/base.py b/rpython/memory/gc/base.py
--- a/rpython/memory/gc/base.py
+++ b/rpython/memory/gc/base.py
@@ -39,8 +39,12 @@
         # all runtime mutable values' setup should happen here
         # and in its overriden versions! for the benefit of test_transformed_gc
         self.finalizer_lock = False
+        self.run_old_style_finalizers = self.AddressDeque()
 
     def mark_finalizer_to_run(self, fq_index, obj):
+        if fq_index == -1:   # backward compatibility with old-style finalizer
+            self.run_old_style_finalizers.append(obj)
+            return
         handlers = self.finalizer_handlers()
         self._adr2deque(handlers[fq_index].deque).append(obj)
 
@@ -67,6 +71,7 @@
                             is_gcarrayofgcptr,
                             finalizer_handlers,
                             destructor_or_custom_trace,
+                            is_old_style_finalizer,
                             offsets_to_gc_pointers,
                             fixed_size, varsize_item_sizes,
                             varsize_offset_to_variable_part,
@@ -81,6 +86,7 @@
                             cannot_pin):
         self.finalizer_handlers = finalizer_handlers
         self.destructor_or_custom_trace = destructor_or_custom_trace
+        self.is_old_style_finalizer = is_old_style_finalizer
         self.is_varsize = is_varsize
         self.has_gcptr_in_varsize = has_gcptr_in_varsize
         self.is_gcarrayofgcptr = is_gcarrayofgcptr
@@ -143,6 +149,8 @@
         size = self.fixed_size(typeid)
         needs_destructor = (bool(self.destructor_or_custom_trace(typeid))
                             and not self.has_custom_trace(typeid))
+        finalizer_is_light = (needs_destructor and
+                              not self.is_old_style_finalizer(typeid))
         contains_weakptr = self.weakpointer_offset(typeid) >= 0
         assert not (needs_destructor and contains_weakptr)
         if self.is_varsize(typeid):
@@ -163,6 +171,7 @@
             else:
                 malloc_fixedsize = self.malloc_fixedsize
             ref = malloc_fixedsize(typeid, size, needs_destructor,
+                                   finalizer_is_light,
                                    contains_weakptr)
         # lots of cast and reverse-cast around...
         ref = llmemory.cast_ptr_to_adr(ref)
@@ -331,6 +340,7 @@
     enumerate_all_roots._annspecialcase_ = 'specialize:arg(1)'
 
     def enum_pending_finalizers(self, callback, arg):
+        self.run_old_style_finalizers.foreach(callback, arg)
         handlers = self.finalizer_handlers()
         i = 0
         while i < len(handlers):
@@ -390,6 +400,13 @@
                 if self._adr2deque(handlers[i].deque).non_empty():
                     handlers[i].trigger()
                 i += 1
+            while self.run_old_style_finalizers.non_empty():
+                obj = self.run_old_style_finalizers.popleft()
+                typeid = self.get_type_id(obj)
+                ll_assert(self.is_old_style_finalizer(typeid),
+                          "bogus old-style finalizer")
+                finalizer = self.destructor_or_custom_trace(typeid)
+                finalizer(obj)
         finally:
             self.finalizer_lock = False
 
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -609,11 +609,25 @@
 
     def malloc_fixedsize(self, typeid, size,
                                needs_destructor=False,
+                               is_finalizer_light=False,
                                contains_weakptr=False):
         size_gc_header = self.gcheaderbuilder.size_gc_header
         totalsize = size_gc_header + size
         rawtotalsize = raw_malloc_usage(totalsize)
         #
+        # If the object needs a finalizer, ask for a rawmalloc.
+        # The following check should be constant-folded.
+        if needs_destructor and not is_finalizer_light:
+            # old-style finalizers only!
+            from rpython.rtyper.lltypesystem import rffi
+            ll_assert(not contains_weakptr,
+                     "'needs_finalizer' and 'contains_weakptr' both specified")
+            obj = self.external_malloc(typeid, 0, alloc_young=False)
+            self.old_objects_with_finalizers.append(obj)
+            self.old_objects_with_finalizers.append(
+                rffi.cast(llmemory.Address, -1))
+            return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+        #
         # If totalsize is greater than nonlarge_max (which should never be
         # the case in practice), ask for a rawmalloc.  The following check
         # should be constant-folded.
@@ -850,6 +864,7 @@
     collect_and_reserve._dont_inline_ = True
 
 
+    # XXX kill alloc_young and make it always True
     def external_malloc(self, typeid, length, alloc_young):
         """Allocate a large object using the ArenaCollection or
         raw_malloc(), possibly as an object with card marking enabled,
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
@@ -1605,9 +1605,12 @@
             ll_call_destructor(destrptr, v, typename)
         fptr = self.transformer.annotate_finalizer(ll_finalizer,
                 [llmemory.Address], lltype.Void)
-        g = destrptr._obj.graph
-        FinalizerAnalyzer(self.translator).check_light_finalizer(g)
-        return fptr
+        try:
+            g = destrptr._obj.graph
+            light = not 
FinalizerAnalyzer(self.translator).analyze_light_finalizer(g)
+        except lltype.DelayedPointer:
+            light = False    # XXX bah, too bad
+        return fptr, light
 
     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
@@ -90,6 +90,10 @@
     def q_destructor_or_custom_trace(self, typeid):
         return self.get(typeid).customfunc
 
+    def q_is_old_style_finalizer(self, typeid):
+        typeinfo = self.get(typeid)
+        return (typeinfo.infobits & T_HAS_OLDSTYLE_FINALIZER) != 0
+
     def q_offsets_to_gc_pointers(self, typeid):
         return self.get(typeid).ofstoptrs
 
@@ -142,6 +146,7 @@
             self.q_is_gcarrayofgcptr,
             self.q_finalizer_handlers,
             self.q_destructor_or_custom_trace,
+            self.q_is_old_style_finalizer,
             self.q_offsets_to_gc_pointers,
             self.q_fixed_size,
             self.q_varsize_item_sizes,
@@ -169,8 +174,9 @@
 T_IS_WEAKREF                = 0x080000
 T_IS_RPYTHON_INSTANCE       = 0x100000 # the type is a subclass of OBJECT
 T_HAS_CUSTOM_TRACE          = 0x200000
-T_HAS_GCPTR                 = 0x400000
-T_KEY_MASK                  = intmask(0xFF000000) # bug detection only
+T_HAS_OLDSTYLE_FINALIZER    = 0x400000
+T_HAS_GCPTR                 = 0x1000000
+T_KEY_MASK                  = intmask(0xFE000000) # bug detection only
 T_KEY_VALUE                 = intmask(0x5A000000) # bug detection only
 
 def _check_valid_type_info(p):
@@ -199,6 +205,9 @@
     if fptrs:
         if "destructor" in fptrs:
             info.customfunc = fptrs["destructor"]
+        if "old_style_finalizer" in fptrs:
+            info.customfunc = fptrs["old_style_finalizer"]
+            infobits |= T_HAS_OLDSTYLE_FINALIZER
     #
     if not TYPE._is_varsize():
         info.fixedsize = llarena.round_up_for_allocation(
@@ -368,11 +377,14 @@
     def special_funcptr_for_type(self, TYPE):
         if TYPE in self._special_funcptrs:
             return self._special_funcptrs[TYPE]
-        fptr1 = self.make_destructor_funcptr_for_type(TYPE)
+        fptr1, is_lightweight = self.make_destructor_funcptr_for_type(TYPE)
         fptr2 = self.make_custom_trace_funcptr_for_type(TYPE)
         result = {}
         if fptr1:
-            result["destructor"] = fptr1
+            if is_lightweight:
+                result["destructor"] = fptr1
+            else:
+                result["old_style_finalizer"] = fptr1
         if fptr2:
             result["custom_trace"] = fptr2
         self._special_funcptrs[TYPE] = result
@@ -386,10 +398,6 @@
         # must be overridden for proper custom tracer support
         return None
 
-    def make_finalizer_trigger(self):
-        # must be overridden for proper finalizer support
-        return None
-
     def initialize_gc_query_function(self, gc):
         gcdata = GCData(self.type_info_group)
         gcdata.set_query_functions(gc)
diff --git a/rpython/memory/gcwrapper.py b/rpython/memory/gcwrapper.py
--- a/rpython/memory/gcwrapper.py
+++ b/rpython/memory/gcwrapper.py
@@ -292,10 +292,10 @@
             DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
             destrgraph = destrptr._obj.graph
         else:
-            return None
+            return None, False
 
         t = self.llinterp.typer.annotator.translator
-        FinalizerAnalyzer(t).check_light_finalizer(destrgraph)
+        is_light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
 
         def ll_destructor(addr):
             try:
@@ -304,7 +304,8 @@
             except llinterp.LLException:
                 raise RuntimeError(
                     "a destructor raised an exception, shouldn't happen")
-        return llhelper(gctypelayout.GCData.CUSTOM_FUNC_PTR, ll_destructor)
+        return (llhelper(gctypelayout.GCData.CUSTOM_FUNC_PTR, ll_destructor),
+                is_light)
 
     def make_custom_trace_funcptr_for_type(self, TYPE):
         from rpython.memory.gctransform.support import get_rtti
diff --git a/rpython/memory/test/gc_test_base.py 
b/rpython/memory/test/gc_test_base.py
--- a/rpython/memory/test/gc_test_base.py
+++ b/rpython/memory/test/gc_test_base.py
@@ -152,6 +152,31 @@
         res = self.interpret(f, [5])
         assert res == 6
 
+    def test_old_style_finalizer(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):
+                llop.gc__collect(lltype.Void)
+                b.num_deleted += 1
+        def f(x):
+            a = A()
+            i = 0
+            while i < x:
+                i += 1
+                a = A()
+            llop.gc__collect(lltype.Void)
+            llop.gc__collect(lltype.Void)
+            return b.num_deleted
+        res = self.interpret(f, [5])
+        assert res == 6
+
     def test_finalizer(self):
         class B(object):
             pass
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -362,9 +362,7 @@
     return func
 
 def must_be_light_finalizer(func):
-    import warnings
-    warnings.warn("@must_be_light_finalizer is implied and has no effect "
-                  "any more", DeprecationWarning)
+    func._must_be_light_finalizer_ = True
     return func
 
 
diff --git a/rpython/translator/backendopt/finalizer.py 
b/rpython/translator/backendopt/finalizer.py
--- a/rpython/translator/backendopt/finalizer.py
+++ b/rpython/translator/backendopt/finalizer.py
@@ -1,6 +1,9 @@
-
 from rpython.translator.backendopt import graphanalyze
 from rpython.rtyper.lltypesystem import lltype
+from rpython.tool.ansi_print import AnsiLogger
+
+log = AnsiLogger("finalizer")
+
 
 class FinalizerError(Exception):
     """__del__() is used for lightweight RPython destructors,
@@ -23,13 +26,19 @@
                      'raw_free', 'adr_eq', 'adr_ne',
                      'debug_print']
 
-    def check_light_finalizer(self, graph):
-        self._origin = graph
-        result = self.analyze_direct_call(graph)
-        del self._origin
-        if result is self.top_result():
-            msg = '%s\nIn %r' % (FinalizerError.__doc__, graph)
-            raise FinalizerError(msg)
+    def analyze_light_finalizer(self, graph):
+        if getattr(graph.func, '_must_be_light_finalizer_', False):
+            self._must_be_light = graph
+            result = self.analyze_direct_call(graph)
+            del self._must_be_light
+            if result is self.top_result():
+                msg = '%s\nIn %r' % (FinalizerError.__doc__, graph)
+                raise FinalizerError(msg)
+        else:
+            result = self.analyze_direct_call(graph)
+            if result is self.top_result():
+                log.red('old-style non-light finalizer: %r' % (graph,))
+        return result
 
     def analyze_simple_operation(self, op, graphinfo):
         if op.opname in self.ok_operations:
@@ -48,9 +57,8 @@
                 # primitive type
                 return self.bottom_result()
 
-        if not hasattr(self, '_origin'):    # for tests
+        if not hasattr(self, '_must_be_light'):
             return self.top_result()
         msg = '%s\nFound this forbidden operation:\n%r\nin %r\nfrom %r' % (
-            FinalizerError.__doc__, op, graphinfo,
-            getattr(self, '_origin', '?'))
+            FinalizerError.__doc__, op, graphinfo, self._must_be_light)
         raise FinalizerError(msg)
diff --git a/rpython/translator/backendopt/test/test_finalizer.py 
b/rpython/translator/backendopt/test/test_finalizer.py
--- a/rpython/translator/backendopt/test/test_finalizer.py
+++ b/rpython/translator/backendopt/test/test_finalizer.py
@@ -26,12 +26,8 @@
             t.view()
         a = FinalizerAnalyzer(t)
         fgraph = graphof(t, func_to_analyze)
-        try:
-            a.check_light_finalizer(fgraph)
-        except FinalizerError as e:
-            print e
-            return a.top_result()   # True
-        return a.bottom_result()    # False
+        result = a.analyze_light_finalizer(fgraph)
+        return result
 
     def test_nothing(self):
         def f():
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to