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