[pypy-commit] pypy gc-del: Copy the logic for young objects with finalizers, for the
arigo
noreply at buildbot.pypy.org
Mon Mar 25 18:55:49 CET 2013
Author: Armin Rigo <arigo at tunes.org>
Branch: gc-del
Changeset: r62746:a5a3becb36e8
Date: 2013-03-25 18:55 +0100
http://bitbucket.org/pypy/pypy/changeset/a5a3becb36e8/
Log: Copy the logic for young objects with finalizers, for the handling
of old objects with finalizers.
diff --git a/rpython/memory/gc/minimark.py b/rpython/memory/gc/minimark.py
--- a/rpython/memory/gc/minimark.py
+++ b/rpython/memory/gc/minimark.py
@@ -940,6 +940,10 @@
# the GCFLAG_VISITED should not be set between collections
ll_assert(self.header(obj).tid & GCFLAG_VISITED == 0,
"unexpected GCFLAG_VISITED")
+ # the GCFLAG_NO_HEAP_PTRS and GCFLAG_HAS_FINALIZER must not be both set
+ ll_assert(self.header(obj).tid & GCFLAG_NO_HEAP_PTRS == 0 or
+ self.header(obj).tid & GCFLAG_HAS_FINALIZER == 0,
+ "GCFLAG_NO_HEAP_PTRS && GCFLAG_HAS_FINALIZER")
# the GCFLAG_CARDS_SET should not be set between collections
ll_assert(self.header(obj).tid & GCFLAG_CARDS_SET == 0,
"unexpected GCFLAG_CARDS_SET")
@@ -967,8 +971,10 @@
i -= 1
def _register_finalizer(self, obj, llfn):
- if self.header(obj).tid & GCFLAG_HAS_FINALIZER:
- return # already have one
+ if self.header(obj).tid & (
+ GCFLAG_HAS_FINALIZER | # obj has already a finalizer
+ GCFLAG_NO_HEAP_PTRS): # immortal object anyway, and we don't
+ return # want confusion in visit()
self.header(obj).tid |= GCFLAG_HAS_FINALIZER
if self.is_young_object(obj):
lst = self.young_objects_with_finalizers
@@ -1989,90 +1995,60 @@
# We try to run the finalizers in a "reasonable" order, like
# CPython does. The details of this algorithm are in
# pypy/doc/discussion/finalizer-order.txt.
- new_with_finalizer = self.AddressDeque()
- marked = self.AddressDeque()
- pending = self.AddressStack()
- self.tmpstack = self.AddressStack()
- while self.objects_with_finalizers.non_empty():
- x = self.objects_with_finalizers.popleft()
- ll_assert(self._finalization_state(x) != 1,
- "bad finalization state 1")
- if self.header(x).tid & GCFLAG_VISITED:
- new_with_finalizer.append(x)
- continue
- marked.append(x)
- pending.append(x)
- while pending.non_empty():
- y = pending.pop()
- state = self._finalization_state(y)
- if state == 0:
- self._bump_finalization_state_from_0_to_1(y)
- self.trace(y, self._append_if_nonnull, pending)
- elif state == 2:
- self._recursively_bump_finalization_state_from_2_to_3(y)
- self._recursively_bump_finalization_state_from_1_to_2(x)
-
- while marked.non_empty():
- x = marked.popleft()
- state = self._finalization_state(x)
- ll_assert(state >= 2, "unexpected finalization state < 2")
- if state == 2:
- self.run_finalizers.append(x)
- # we must also fix the state from 2 to 3 here, otherwise
- # we leave the GCFLAG_FINALIZATION_ORDERING bit behind
- # which will confuse the next collection
- self._recursively_bump_finalization_state_from_2_to_3(x)
+ #
+ ll_assert(not self.objects_to_trace.non_empty(),
+ "objects_to_trace non empty [deal_old_finalizer]")
+ #
+ old_objs = self.old_objects_with_finalizers
+ self.old_objects_with_finalizers = self.AddressStack()
+ finalizer_funcs = self.AddressDict()
+ #
+ while old_objs.non_empty():
+ func = old_objs.pop()
+ obj = old_objs.pop()
+ ll_assert(bool(self.header(obj).tid & GCFLAG_HAS_FINALIZER),
+ "lost GCFLAG_HAS_FINALIZER")
+ #
+ if self.header(obj).tid & GCFLAG_VISITED:
+ # surviving
+ self.old_objects_with_finalizers.append(obj)
+ self.old_objects_with_finalizers.append(func)
+ #
else:
- new_with_finalizer.append(x)
-
- self.tmpstack.delete()
- pending.delete()
- marked.delete()
- self.objects_with_finalizers.delete()
- self.objects_with_finalizers = new_with_finalizer
-
- def _append_if_nonnull(pointer, stack):
- stack.append(pointer.address[0])
- _append_if_nonnull = staticmethod(_append_if_nonnull)
-
- def _finalization_state(self, obj):
- tid = self.header(obj).tid
- if tid & GCFLAG_VISITED:
- if tid & GCFLAG_FINALIZATION_ORDERING:
- return 2
+ # dying
+ self.objects_to_trace.append(obj)
+ finalizer_funcs.setitem(obj, func)
+ #
+ # Now follow all the refs
+ finalizers_scheduled = self.AddressStack()
+ pending = self.objects_to_trace
+ while pending.non_empty():
+ obj = pending.pop()
+ if obj:
+ #
+ if self.header(obj).tid & GCFLAG_HAS_FINALIZER:
+ self.header(obj).tid &= ~GCFLAG_HAS_FINALIZER
+ pending.append(obj)
+ pending.append(NULL) # marker
+ #
+ self.visit(obj)
+ #
else:
- return 3
- else:
- if tid & GCFLAG_FINALIZATION_ORDERING:
- return 1
- else:
- return 0
-
- def _bump_finalization_state_from_0_to_1(self, obj):
- ll_assert(self._finalization_state(obj) == 0,
- "unexpected finalization state != 0")
- hdr = self.header(obj)
- hdr.tid |= GCFLAG_FINALIZATION_ORDERING
-
- def _recursively_bump_finalization_state_from_2_to_3(self, obj):
- ll_assert(self._finalization_state(obj) == 2,
- "unexpected finalization state != 2")
- pending = self.tmpstack
- ll_assert(not pending.non_empty(), "tmpstack not empty")
- pending.append(obj)
- while pending.non_empty():
- y = pending.pop()
- hdr = self.header(y)
- if hdr.tid & GCFLAG_FINALIZATION_ORDERING: # state 2 ?
- hdr.tid &= ~GCFLAG_FINALIZATION_ORDERING # change to state 3
- self.trace(y, self._append_if_nonnull, pending)
-
- def _recursively_bump_finalization_state_from_1_to_2(self, obj):
- # recursively convert objects from state 1 to state 2.
- # The call to visit_all_objects() will add the GCFLAG_VISITED
- # recursively.
- self.objects_to_trace.append(obj)
- self.visit_all_objects()
+ # seen a NULL marker
+ obj = pending.pop()
+ finalizers_scheduled.append(obj)
+ #
+ # Copy the objects scheduled into 'run_finalizers_queue', in
+ # reverse order.
+ while finalizers_scheduled.non_empty():
+ obj = finalizers_scheduled.pop()
+ func = finalizer_funcs.get(obj)
+ ll_assert(func != NULL, "lost finalizer [2]")
+ self.run_finalizers_queue.append(obj)
+ self.run_finalizers_funcs.append(func)
+ finalizers_scheduled.delete()
+ finalizer_funcs.delete()
+ old_objs.delete()
# ----------
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
@@ -8,6 +8,7 @@
from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.rlib.objectmodel import we_are_translated
from rpython.rlib.objectmodel import compute_unique_id
+from rpython.rlib.objectmodel import keepalive_until_here
from rpython.rlib import rgc
from rpython.rlib.rstring import StringBuilder
from rpython.rlib.rarithmetic import LONG_BIT
@@ -158,14 +159,13 @@
pass
b = B()
b.nextid = 0
- b.num_finalized = -42
+ b.num_finalized = 0
class A(object):
def __init__(self):
self.id = b.nextid
b.nextid += 1
def finalizer(self):
b.num_finalized += 1
- print "BIP", b.num_finalized
def allocate(x):
i = 0
while i < x:
@@ -173,14 +173,41 @@
a = A()
rgc.register_finalizer(a.finalizer)
def f(x):
- print 'START'
+ 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_old(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
+ next = None
+ while i < x:
+ i += 1
+ a = A()
+ a.next = next
+ next = a
+ rgc.register_finalizer(a.finalizer)
+ llop.gc__collect(lltype.Void) # all objects are now old
+ keepalive_until_here(next)
+ def f(x):
b.num_finalized = 0
allocate(x)
- print 'XX', b.num_finalized
llop.gc__collect(lltype.Void)
- print 'XX', b.num_finalized
llop.gc__collect(lltype.Void)
- print 'XX', b.num_finalized
return b.num_finalized
res = self.interpret(f, [6])
assert res == 6
More information about the pypy-commit
mailing list