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