Author: Armin Rigo <ar...@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: _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit