Author: Armin Rigo <[email protected]>
Branch: gc-del-3
Changeset: r84129:7edef1bf570b
Date: 2016-05-02 18:57 +0200
http://bitbucket.org/pypy/pypy/changeset/7edef1bf570b/

Log:    in-progress

diff --git a/pypy/doc/discussion/finalizer-order.rst 
b/pypy/doc/discussion/finalizer-order.rst
--- a/pypy/doc/discussion/finalizer-order.rst
+++ b/pypy/doc/discussion/finalizer-order.rst
@@ -90,10 +90,10 @@
 To find the queued items, call ``fin.next_dead()`` repeatedly.  It
 returns the next queued item, or ``None`` when the queue is empty.
 
-It is not allowed to cumulate several ``FinalizerQueue`` instances for
-objects of the same class.  Calling ``fin.register_finalizer(obj)``
-several times with the same arguments is fine (and will only register
-``obj`` once).
+It is allowed in theory to cumulate several different
+``FinalizerQueue`` instances for objects of the same class, and
+(always in theory) the same ``obj`` could be registered several times
+in the same queue, or in several queues.  This is not tested though.
 
 
 Ordering of finalizers
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
@@ -60,8 +60,7 @@
 
     def set_query_functions(self, is_varsize, has_gcptr_in_varsize,
                             is_gcarrayofgcptr,
-                            getfinalizer,
-                            getlightfinalizer,
+                            destructor_or_custom_trace,
                             offsets_to_gc_pointers,
                             fixed_size, varsize_item_sizes,
                             varsize_offset_to_variable_part,
@@ -74,8 +73,7 @@
                             fast_path_tracing,
                             has_gcptr,
                             cannot_pin):
-        self.getfinalizer = getfinalizer
-        self.getlightfinalizer = getlightfinalizer
+        self.destructor_or_custom_trace = destructor_or_custom_trace
         self.is_varsize = is_varsize
         self.has_gcptr_in_varsize = has_gcptr_in_varsize
         self.is_gcarrayofgcptr = is_gcarrayofgcptr
@@ -136,13 +134,13 @@
         the four malloc_[fixed,var]size[_clear]() functions.
         """
         size = self.fixed_size(typeid)
-        needs_finalizer = bool(self.getfinalizer(typeid))
-        finalizer_is_light = bool(self.getlightfinalizer(typeid))
+        needs_destructor = (bool(self.destructor_or_custom_trace(typeid))
+                            and not self.has_custom_trace(typeid))
         contains_weakptr = self.weakpointer_offset(typeid) >= 0
-        assert not (needs_finalizer and contains_weakptr)
+        assert not (needs_destructor and contains_weakptr)
         if self.is_varsize(typeid):
             assert not contains_weakptr
-            assert not needs_finalizer
+            assert not needs_destructor
             itemsize = self.varsize_item_sizes(typeid)
             offset_to_length = self.varsize_offset_to_length(typeid)
             if self.malloc_zero_filled:
@@ -157,8 +155,7 @@
                 malloc_fixedsize = self.malloc_fixedsize_clear
             else:
                 malloc_fixedsize = self.malloc_fixedsize
-            ref = malloc_fixedsize(typeid, size, needs_finalizer,
-                                   finalizer_is_light,
+            ref = malloc_fixedsize(typeid, size, needs_destructor,
                                    contains_weakptr)
         # lots of cast and reverse-cast around...
         ref = llmemory.cast_ptr_to_adr(ref)
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
@@ -372,10 +372,19 @@
 
         self.gc_state = STATE_SCANNING
         #
-        # A list of all objects with finalizers (these are never young).
-        self.objects_with_finalizers = self.AddressDeque()
-        self.young_objects_with_light_finalizers = self.AddressStack()
-        self.old_objects_with_light_finalizers = self.AddressStack()
+        # Two lists of all objects with finalizers.  Actually they are lists
+        # of pairs (finalization_queue_nr, object).  "probably young objects"
+        # are all traced and moved to the "old" list by the next minor
+        # collection.
+        self.probably_young_objects_with_finalizers = self.AddressDeque()
+        self.old_objects_with_finalizers = self.AddressDeque()
+        p = lltype.malloc(self._ADDRARRAY, 1, flavor='raw',
+                          track_allocation=False)
+        self.singleaddr = llmemory.cast_ptr_to_adr(p)
+        #
+        # Two lists of all objects with destructors.
+        self.young_objects_with_destructors = self.AddressStack()
+        self.old_objects_with_destructors = self.AddressStack()
         #
         # Two lists of the objects with weakrefs.  No weakref can be an
         # old object weakly pointing to a young object: indeed, weakrefs
@@ -599,25 +608,16 @@
 
 
     def malloc_fixedsize(self, typeid, size,
-                               needs_finalizer=False,
-                               is_finalizer_light=False,
+                               needs_destructor=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_finalizer and not is_finalizer_light:
-            ll_assert(not contains_weakptr,
-                     "'needs_finalizer' and 'contains_weakptr' both specified")
-            obj = self.external_malloc(typeid, 0, alloc_young=False)
-            self.objects_with_finalizers.append(obj)
-        #
         # 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.
-        elif rawtotalsize > self.nonlarge_max:
+        if rawtotalsize > self.nonlarge_max:
             ll_assert(not contains_weakptr,
                       "'contains_weakptr' specified for a large object")
             obj = self.external_malloc(typeid, 0, alloc_young=True)
@@ -639,14 +639,14 @@
             # Build the object.
             llarena.arena_reserve(result, totalsize)
             obj = result + size_gc_header
-            if is_finalizer_light:
-                self.young_objects_with_light_finalizers.append(obj)
             self.init_gc_object(result, typeid, flags=0)
-            #
-            # If it is a weakref, record it (check constant-folded).
-            if contains_weakptr:
-                self.young_objects_with_weakrefs.append(obj)
         #
+        # If it is a weakref or has a lightweight destructor, record it
+        # (checks constant-folded).
+        if needs_destructor:
+            self.young_objects_with_destructors.append(obj)
+        if contains_weakptr:
+            self.young_objects_with_weakrefs.append(obj)
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
@@ -1632,6 +1632,11 @@
         if self.rrc_enabled:
             self.rrc_minor_collection_trace()
         #
+        # visit the "probably young" objects with finalizers.  They
+        # always all survive.
+        if self.probably_young_objects_with_finalizers.non_empty():
+            self.deal_with_young_objects_with_finalizers()
+        #
         while True:
             # If we are using card marking, do a partial trace of the arrays
             # that are flagged with GCFLAG_CARDS_SET.
@@ -1657,8 +1662,8 @@
         # weakrefs' targets.
         if self.young_objects_with_weakrefs.non_empty():
             self.invalidate_young_weakrefs()
-        if self.young_objects_with_light_finalizers.non_empty():
-            self.deal_with_young_objects_with_finalizers()
+        if self.young_objects_with_destructors.non_empty():
+            self.deal_with_young_objects_with_destructors()
         #
         # Clear this mapping.  Without pinned objects we just clear the dict
         # as all objects in the nursery are dragged out of the nursery and, if
@@ -2220,7 +2225,10 @@
                 if self.rrc_enabled:
                     self.rrc_major_collection_trace()
                 #
-                if self.objects_with_finalizers.non_empty():
+                ll_assert(not (self.probably_young_objects_with_finalizers
+                               .non_empty()),
+                    "probably_young_objects_with_finalizers should be empty")
+                if self.old_objects_with_finalizers.non_empty():
                     self.deal_with_objects_with_finalizers()
                 elif self.old_objects_with_weakrefs.non_empty():
                     # Weakref support: clear the weak pointers to dying objects
@@ -2236,9 +2244,9 @@
                 self.more_objects_to_trace.delete()
 
                 #
-                # Light finalizers
-                if self.old_objects_with_light_finalizers.non_empty():
-                    self.deal_with_old_objects_with_finalizers()
+                # Destructors
+                if self.old_objects_with_destructors.non_empty():
+                    self.deal_with_old_objects_with_destructors()
                 # objects_to_trace processed fully, can move on to sweeping
                 self.ac.mass_free_prepare()
                 self.start_free_rawmalloc_objects()
@@ -2572,10 +2580,9 @@
     # ----------
     # Finalizers
 
-    def deal_with_young_objects_with_finalizers(self):
-        """ This is a much simpler version of dealing with finalizers
-        and an optimization - we can reasonably assume that those finalizers
-        don't do anything fancy and *just* call them. Among other things
+    def deal_with_young_objects_with_destructors(self):
+        """We can reasonably assume that destructors don't do
+        anything fancy and *just* call them. Among other things
         they won't resurrect objects
         """
         while self.young_objects_with_light_finalizers.non_empty():
@@ -2588,10 +2595,9 @@
                 obj = self.get_forwarding_address(obj)
                 self.old_objects_with_light_finalizers.append(obj)
 
-    def deal_with_old_objects_with_finalizers(self):
-        """ This is a much simpler version of dealing with finalizers
-        and an optimization - we can reasonably assume that those finalizers
-        don't do anything fancy and *just* call them. Among other things
+    def deal_with_old_objects_with_destructors(self):
+        """We can reasonably assume that destructors don't do
+        anything fancy and *just* call them. Among other things
         they won't resurrect objects
         """
         new_objects = self.AddressStack()
@@ -2608,6 +2614,16 @@
         self.old_objects_with_light_finalizers.delete()
         self.old_objects_with_light_finalizers = new_objects
 
+    def deal_with_young_objects_with_finalizers(self):
+        while self.probably_young_objects_with_finalizers.non_empty():
+            obj = self.probably_young_objects_with_finalizers.popleft()
+            fin_nr = self.probably_young_objects_with_finalizers.popleft()
+            singleaddr.address[0] = obj
+            self._trace_drag_out1(singleaddr)
+            obj = singleaddr.address[0]
+            self.old_objects_with_light_finalizers.append(obj)
+            self.old_objects_with_light_finalizers.append(fin_nr)
+
     def deal_with_objects_with_finalizers(self):
         # Walk over list of objects with finalizers.
         # If it is not surviving, add it to the list of to-be-called
@@ -2814,9 +2830,6 @@
             self.rrc_o_list_old   = self.AddressStack()
             self.rrc_p_dict       = self.AddressDict()  # non-nursery keys only
             self.rrc_p_dict_nurs  = self.AddressDict()  # nursery keys only
-            p = lltype.malloc(self._ADDRARRAY, 1, flavor='raw',
-                              track_allocation=False)
-            self.rrc_singleaddr = llmemory.cast_ptr_to_adr(p)
             self.rrc_dealloc_trigger_callback = dealloc_trigger_callback
             self.rrc_dealloc_pending = self.AddressStack()
             self.rrc_enabled = True
@@ -2886,7 +2899,7 @@
         self.rrc_p_dict_nurs.delete()
         self.rrc_p_dict_nurs = self.AddressDict(length_estimate)
         self.rrc_p_list_young.foreach(self._rrc_minor_trace,
-                                      self.rrc_singleaddr)
+                                      self.singleaddr)
 
     def _rrc_minor_trace(self, pyobject, singleaddr):
         from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
@@ -2899,7 +2912,7 @@
             # force the corresponding object to be alive
             intobj = self._pyobj(pyobject).ob_pypy_link
             singleaddr.address[0] = llmemory.cast_int_to_adr(intobj)
-            self._trace_drag_out(singleaddr, llmemory.NULL)
+            self._trace_drag_out1(singleaddr)
 
     def rrc_minor_collection_free(self):
         ll_assert(self.rrc_p_dict_nurs.length() == 0, "p_dict_nurs not empty 
1")
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
@@ -1513,7 +1513,7 @@
         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)
diff --git a/rpython/memory/gctypelayout.py b/rpython/memory/gctypelayout.py
--- a/rpython/memory/gctypelayout.py
+++ b/rpython/memory/gctypelayout.py
@@ -17,16 +17,17 @@
 
     OFFSETS_TO_GC_PTR = lltype.Array(lltype.Signed)
 
-    # A custom tracer (CT), enumerates the addresses that contain GCREFs.
-    # It is called with the object as first argument, and the previous
-    # returned address (or NULL the first time) as the second argument.
-    FINALIZER_FUNC = lltype.FuncType([llmemory.Address], lltype.Void)
-    FINALIZER = lltype.Ptr(FINALIZER_FUNC)
+    # A CUSTOM_FUNC is either a destructor, or a custom tracer.
+    # A destructor is called when the object is about to be freed.
+    # A custom tracer (CT) enumerates the addresses that contain GCREFs.
+    # Both are called with the address of the object as only argument.
+    CUSTOM_FUNC = lltype.FuncType([llmemory.Address], lltype.Void)
+    CUSTOM_FUNC_PTR = lltype.Ptr(CUSTOM_FUNC)
 
     # structure describing the layout of a typeid
     TYPE_INFO = lltype.Struct("type_info",
         ("infobits",       lltype.Signed),    # combination of the T_xxx consts
-        ("finalizer",      FINALIZER),
+        ("customfunc",     CUSTOM_FUNC_PTR),
         ("fixedsize",      lltype.Signed),
         ("ofstoptrs",      lltype.Ptr(OFFSETS_TO_GC_PTR)),
         hints={'immutable': True},
@@ -80,16 +81,10 @@
     def q_cannot_pin(self, typeid):
         typeinfo = self.get(typeid)
         ANY = (T_HAS_GCPTR | T_IS_WEAKREF)
-        return (typeinfo.infobits & ANY) != 0 or bool(typeinfo.finalizer)
+        return (typeinfo.infobits & ANY) != 0 or bool(typeinfo.customfunc)
 
-    def q_finalizer(self, typeid):
-        return self.get(typeid).finalizer
-
-    def q_light_finalizer(self, typeid):
-        typeinfo = self.get(typeid)
-        if typeinfo.infobits & T_HAS_LIGHTWEIGHT_FINALIZER:
-            return typeinfo.finalizer
-        return lltype.nullptr(GCData.FINALIZER_FUNC)
+    def q_destructor_or_custom_trace(self, typeid):
+        return self.get(typeid).customfunc
 
     def q_offsets_to_gc_pointers(self, typeid):
         return self.get(typeid).ofstoptrs
@@ -141,8 +136,7 @@
             self.q_is_varsize,
             self.q_has_gcptr_in_varsize,
             self.q_is_gcarrayofgcptr,
-            self.q_finalizer,
-            self.q_light_finalizer,
+            self.q_destructor_or_custom_trace,
             self.q_offsets_to_gc_pointers,
             self.q_fixed_size,
             self.q_varsize_item_sizes,
@@ -170,9 +164,8 @@
 T_IS_WEAKREF                = 0x080000
 T_IS_RPYTHON_INSTANCE       = 0x100000 # the type is a subclass of OBJECT
 T_HAS_CUSTOM_TRACE          = 0x200000
-T_HAS_LIGHTWEIGHT_FINALIZER = 0x400000
-T_HAS_GCPTR                 = 0x1000000
-T_KEY_MASK                  = intmask(0xFE000000) # bug detection only
+T_HAS_GCPTR                 = 0x400000
+T_KEY_MASK                  = intmask(0xFF000000) # bug detection only
 T_KEY_VALUE                 = intmask(0x5A000000) # bug detection only
 
 def _check_valid_type_info(p):
@@ -199,11 +192,8 @@
     #
     fptrs = builder.special_funcptr_for_type(TYPE)
     if fptrs:
-        if "finalizer" in fptrs:
-            info.finalizer = fptrs["finalizer"]
-        if "light_finalizer" in fptrs:
-            info.finalizer = fptrs["light_finalizer"]
-            infobits |= T_HAS_LIGHTWEIGHT_FINALIZER
+        if "destructor" in fptrs:
+            info.customfunc = fptrs["destructor"]
     #
     if not TYPE._is_varsize():
         info.fixedsize = llarena.round_up_for_allocation(
@@ -373,22 +363,19 @@
     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)
         result = {}
         if fptr1:
-            if is_lightweight:
-                result["light_finalizer"] = fptr1
-            else:
-                result["finalizer"] = fptr1
+            result["destructor"] = fptr1
         if fptr2:
             result["custom_trace"] = fptr2
         self._special_funcptrs[TYPE] = result
         return result
 
-    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
@@ -228,7 +228,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'):
@@ -236,18 +236,19 @@
             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)
-        def ll_finalizer(addr):
+        FinalizerAnalyzer(t).check_light_finalizer(destrgraph)
+
+        def ll_destructor(addr):
             try:
                 v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG)
                 self.llinterp.eval_graph(destrgraph, [v], recursive=True)
             except llinterp.LLException:
                 raise RuntimeError(
-                    "a finalizer raised an exception, shouldn't happen")
-        return llhelper(gctypelayout.GCData.FINALIZER, ll_finalizer), light
+                    "a destructor raised an exception, shouldn't happen")
+        return llhelper(gctypelayout.GCData.CUSTOM_FUNC_PTR, ll_destructor)
 
     def make_custom_trace_funcptr_for_type(self, TYPE):
         from rpython.memory.gctransform.support import get_rtti
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -369,25 +369,43 @@
 
 class FinalizerQueue(object):
     """A finalizer queue.  See pypy/doc/discussion/finalizer-order.rst.
+    Note: only works with the framework GCs (like minimark).  It is
+    ignored with Boehm or with refcounting (used by tests).
     """
     # Must be subclassed, and the subclass needs these attributes:
     #
-    #    base_class:
-    #        the base class (or only class) of finalized objects
+    #    Class:
+    #        the class (or base class) of finalized objects
     #
     #    def finalizer_trigger(self):
     #        called to notify that new items have been put in the queue
 
+    def _freeze_(self):
+        return True
+
+    @specialize.arg(0)
     def next_dead(self):
-        "NOT_RPYTHON: special-cased below"
+        if we_are_translated():
+            from rpython.rtyper.lltypesystem.lloperation import llop
+            from rpython.rtyper.rclass import OBJECTPTR
+            from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
+            ptr = llop.gc_fq_next_dead(OBJECTPTR, self)
+            return cast_base_ptr_to_instance(self.Class, ptr)
         try:
             return self._queue.popleft()
         except (AttributeError, IndexError):
             return None
 
+    @specialize.arg(0)
     def register_finalizer(self, obj):
-        "NOT_RPYTHON: special-cased below"
-        assert isinstance(obj, self.base_class)
+        assert isinstance(obj, self.Class)
+        if we_are_translated():
+            from rpython.rtyper.lltypesystem.lloperation import llop
+            from rpython.rtyper.rclass import OBJECTPTR
+            from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr
+            ptr = cast_instance_to_base_ptr(obj)
+            llop.gc_fq_register(lltype.Void, self, ptr)
+            return
 
         if hasattr(obj, '__enable_del_for_id'):
             return    # already called
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
@@ -3,8 +3,8 @@
 from rpython.rtyper.lltypesystem import lltype
 
 class FinalizerError(Exception):
-    """ __del__ marked as lightweight finalizer, but the analyzer did
-    not agree
+    """__del__() is used for lightweight RPython destructors,
+    but the FinalizerAnalyzer found that it is not lightweight.
     """
 
 class FinalizerAnalyzer(graphanalyze.BoolGraphAnalyzer):
@@ -20,12 +20,10 @@
                      'direct_ptradd', 'force_cast', 'track_alloc_stop',
                      'raw_free', 'adr_eq', 'adr_ne']
 
-    def analyze_light_finalizer(self, graph):
+    def check_light_finalizer(self, graph):
         result = self.analyze_direct_call(graph)
-        if (result is self.top_result() and
-            getattr(graph.func, '_must_be_light_finalizer_', False)):
+        if result is self.top_result():
             raise FinalizerError(FinalizerError.__doc__, graph)
-        return result
 
     def analyze_simple_operation(self, op, graphinfo):
         if op.opname in self.ok_operations:
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to