Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r97188:f454ba4d28f6
Date: 2019-08-14 10:54 +0200
http://bitbucket.org/pypy/pypy/changeset/f454ba4d28f6/
Log: Refactored rrc to support multiple implementations
diff too long, truncating to 2000 out of 2308 lines
diff --git a/rpython/config/translationoption.py
b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -105,14 +105,14 @@
"asmgcc": [("translation.gctransformer", "framework"),
("translation.backend", "c")],
}),
- ChoiceOption("cpyextgc", "Garbage Collection Strategy for cpyext",
- ["boehm", "trialdeletion", "none"],
- default="trialdeletion",
+ ChoiceOption("rrcgc", "Garbage Collection Strategy for raw refcounted
objects in cpyext",
+ ["mark", "incmark", "none"],
+ default="mark",
requires={
- "boehm": [("translation.gc", "incminimark")],
- "trialdeletion": [("translation.gc", "incminimark")],
+ "mark": [("translation.gc", "incminimark")],
+ "incmark": [("translation.gc", "incminimark")],
},
- cmdline="--cpyextgc"),
+ cmdline="--rrcgc"),
# other noticeable options
BoolOption("thread", "enable use of threading primitives",
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
@@ -75,7 +75,7 @@
from rpython.rlib.objectmodel import specialize
from rpython.rlib import rgc
from rpython.memory.gc.minimarkpage import out_of_memory
-from rpython.rtyper.lltypesystem import rffi
+from rpython.memory.gc.rrc.mark import RawRefCountBaseGC, RawRefCountMarkGC
#
# Handles the objects in 2 generations:
@@ -198,6 +198,7 @@
('forw', llmemory.Address))
FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB)
NURSARRAY = lltype.Array(llmemory.Address)
+ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
# ____________________________________________________________
@@ -400,8 +401,7 @@
# 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)
+ p = lltype.malloc(ADDRARRAY, 1, flavor='raw', track_allocation=False)
self.singleaddr = llmemory.cast_ptr_to_adr(p)
#
# Two lists of all objects with destructors.
@@ -794,7 +794,8 @@
else:
# This does a complete minor and major collection.
self.minor_and_major_collection()
- self.rrc_invoke_callback()
+ if self.rrc_enabled:
+ self.rrc_gc.invoke_callback()
def collect_step(self):
"""
@@ -807,7 +808,8 @@
old_state = self.gc_state
self._minor_collection()
self.major_collection_step()
- self.rrc_invoke_callback()
+ if self.rrc_enabled:
+ self.rrc_gc.invoke_callback()
return rgc._encode_states(old_state, self.gc_state)
def minor_collection_with_major_progress(self, extrasize=0,
@@ -848,7 +850,8 @@
self._minor_collection()
self.major_collection_step(extrasize)
- self.rrc_invoke_callback()
+ if self.rrc_enabled:
+ self.rrc_gc.invoke_callback()
def collect_and_reserve(self, totalsize):
@@ -903,7 +906,7 @@
self.minor_collection_with_major_progress()
else:
# Nursery too full again. This is likely because of
- # execute_finalizers() or rrc_invoke_callback().
+ # execute_finalizers() or rrc_gc.invoke_callback()().
# we need to fix it with another call to minor_collection()
# ---this time only the minor part so that we are sure that
# the nursery is empty (apart from pinned objects).
@@ -1768,7 +1771,7 @@
#
# visit the P list from rawrefcount, if enabled.
if self.rrc_enabled:
- self.rrc_minor_collection_trace()
+ self.rrc_gc.minor_trace()
#
# visit the "probably young" objects with finalizers. They
# all survive, except if IGNORE_FINALIZER is set.
@@ -1820,7 +1823,7 @@
#
# visit the P and O lists from rawrefcount, if enabled.
if self.rrc_enabled:
- self.rrc_minor_collection_free()
+ self.rrc_gc.minor_collection_free()
#
# Walk the list of young raw-malloced objects, and either free
# them or make them old.
@@ -2403,7 +2406,7 @@
self.visit_all_objects()
#
if self.rrc_enabled:
- self.rrc_major_collection_trace()
+ self.rrc_gc.major_collection_trace()
#
ll_assert(not (self.probably_young_objects_with_finalizers
.non_empty()),
@@ -2445,7 +2448,7 @@
self.updated_old_objects_pointing_to_pinned = True
#
if self.rrc_enabled:
- self.rrc_major_collection_free()
+ self.rrc_gc.major_collection_free()
#
self.stat_ac_arenas_count = self.ac.arenas_count
self.stat_rawmalloced_total_size = self.rawmalloced_total_size
@@ -2720,7 +2723,7 @@
hdr.tid |= GCFLAG_VISITED | GCFLAG_TRACK_YOUNG_PTRS
if self.rrc_enabled and \
- self.rrc_state == self.RAWREFCOUNT_STATE_MARKING:
+ self.rrc_gc.state == RawRefCountBaseGC.STATE_MARKING:
hdr.tid |= GCFLAG_GARBAGE
if self.has_gcptr(llop.extract_ushort(llgroup.HALFWORD, hdr.tid)):
@@ -3072,935 +3075,96 @@
# ----------
# RawRefCount
+ PYOBJ_HDR = RawRefCountBaseGC.PYOBJ_HDR
+ PYOBJ_HDR_PTR = RawRefCountBaseGC.PYOBJ_HDR_PTR
+ RAWREFCOUNT_DEALLOC_TRIGGER = RawRefCountBaseGC.RAWREFCOUNT_DEALLOC_TRIGGER
+ RAWREFCOUNT_VISIT = RawRefCountBaseGC.RAWREFCOUNT_VISIT
+ RAWREFCOUNT_TRAVERSE = RawRefCountBaseGC.RAWREFCOUNT_TRAVERSE
+ PYOBJ_GC_HDR_PTR = RawRefCountBaseGC.PYOBJ_GC_HDR_PTR
+ PYOBJ_GC_HDR = RawRefCountBaseGC.PYOBJ_GC_HDR
+ RAWREFCOUNT_GC_AS_PYOBJ = RawRefCountBaseGC.RAWREFCOUNT_GC_AS_PYOBJ
+ RAWREFCOUNT_PYOBJ_AS_GC = RawRefCountBaseGC.RAWREFCOUNT_PYOBJ_AS_GC
+ RAWREFCOUNT_FINALIZER_TYPE = RawRefCountBaseGC.RAWREFCOUNT_FINALIZER_TYPE
+ RAWREFCOUNT_CLEAR_WR_TYPE = RawRefCountBaseGC.RAWREFCOUNT_CLEAR_WR_TYPE
+ RAWREFCOUNT_MAYBE_UNTRACK_TUPLE = \
+ RawRefCountBaseGC.RAWREFCOUNT_MAYBE_UNTRACK_TUPLE
rrc_enabled = False
-
- # Default state, no rawrefcount specific code is executed during normal
marking.
- RAWREFCOUNT_STATE_DEFAULT = 0
-
- # Here cyclic garbage only reachable from legacy finalizers is marked.
- RAWREFCOUNT_STATE_MARKING = 1
-
- # The state in which cyclic garbage with legacy finalizers is traced.
- # Do not mark objects during this state, because we remove the flag
- # during tracing and we do not want to trace those objects again. Also
- # during this phase no new objects can be marked, as we are only building
- # the list of cyclic garbage.
- RAWREFCOUNT_STATE_GARBAGE = 2
-
- _ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
- PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
- ('c_ob_refcnt', lltype.Signed),
- ('c_ob_pypy_link', lltype.Signed))
- PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
- RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
- RAWREFCOUNT_VISIT = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP],
- rffi.INT_real))
- RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR,
- RAWREFCOUNT_VISIT,
- rffi.VOIDP],
- lltype.Void))
- PYOBJ_GC_HDR_PTR = lltype.Ptr(lltype.ForwardReference())
- PYOBJ_GC_HDR = lltype.Struct('PyGC_Head',
- ('c_gc_next', PYOBJ_GC_HDR_PTR),
- ('c_gc_prev', PYOBJ_GC_HDR_PTR),
- ('c_gc_refs', lltype.Signed))
- PYOBJ_GC_HDR_PTR.TO.become(PYOBJ_GC_HDR)
- RAWREFCOUNT_GC_AS_PYOBJ = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
- PYOBJ_HDR_PTR))
- RAWREFCOUNT_PYOBJ_AS_GC = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR],
- PYOBJ_GC_HDR_PTR))
- RAWREFCOUNT_FINALIZER_TYPE = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
- lltype.Signed))
- RAWREFCOUNT_CLEAR_WR_TYPE = lltype.Ptr(lltype.FuncType([llmemory.GCREF],
- lltype.Void))
- RAWREFCOUNT_MAYBE_UNTRACK_TUPLE = \
- lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR], lltype.Signed))
- RAWREFCOUNT_FINALIZER_NONE = 0
- RAWREFCOUNT_FINALIZER_MODERN = 1
- RAWREFCOUNT_FINALIZER_LEGACY = 2
- RAWREFCOUNT_REFS_SHIFT = 1
- RAWREFCOUNT_REFS_MASK_FINALIZED = 1
- RAWREFCOUNT_REFS_UNTRACKED = -2 << RAWREFCOUNT_REFS_SHIFT
-
- def _pyobj(self, pyobjaddr):
- return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
- def _pygchdr(self, pygchdraddr):
- return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR)
+ rrc_gc = None
def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse,
pyobj_list, tuple_list, gc_as_pyobj, pyobj_as_gc,
finalizer_type, clear_weakref_callback,
tuple_maybe_untrack):
- # see pypy/doc/discussion/rawrefcount.rst
if not self.rrc_enabled:
- self.rrc_p_list_young = self.AddressStack()
- self.rrc_p_list_old = self.AddressStack()
- self.rrc_o_list_young = self.AddressStack()
- 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
- self.rrc_dealloc_trigger_callback = dealloc_trigger_callback
- self.rrc_dealloc_pending = self.AddressStack()
- self.rrc_refcnt_dict = self.AddressDict()
- self.rrc_tp_traverse = tp_traverse
- self.rrc_pyobj_list = self._pygchdr(pyobj_list)
- self.rrc_tuple_list = self._pygchdr(tuple_list)
- self.rrc_pyobj_old_list = self._rrc_gc_list_new()
- self.rrc_pyobj_isolate_list = self._rrc_gc_list_new()
- self.rrc_pyobj_dead_list = self._rrc_gc_list_new()
- self.rrc_pyobj_garbage_list = self._rrc_gc_list_new()
- self.rrc_garbage_to_trace = self.AddressStack()
- self.rrc_gc_as_pyobj = gc_as_pyobj
- self.rrc_pyobj_as_gc = pyobj_as_gc
- self.rrc_finalizer_type = finalizer_type
- self.rrc_clear_weakref_callback = clear_weakref_callback
- self.rrc_tuple_maybe_untrack = tuple_maybe_untrack
+ gc_flags = (GCFLAG_VISITED_RMY, GCFLAG_VISITED,
+ GCFLAG_NO_HEAP_PTRS, GCFLAG_GARBAGE)
+ self.rrc_gc.init(self, gc_flags, dealloc_trigger_callback,
+ tp_traverse, pyobj_list, tuple_list, gc_as_pyobj,
+ pyobj_as_gc, finalizer_type,
+ clear_weakref_callback, tuple_maybe_untrack)
self.rrc_enabled = True
- self.rrc_cycle_enabled = True
- self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
- def check_no_more_rawrefcount_state(self):
- "NOT_RPYTHON: for tests"
- assert self.rrc_p_list_young.length() == 0
- assert self.rrc_p_list_old .length() == 0
- assert self.rrc_o_list_young.length() == 0
- assert self.rrc_o_list_old .length() == 0
- def check_value_is_null(key, value, ignore):
- assert value == llmemory.NULL
- self.rrc_p_dict.foreach(check_value_is_null, None)
- self.rrc_p_dict_nurs.foreach(check_value_is_null, None)
+ def activate_rawrefcount_cycle(self):
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ self.rrc_gc.rrc_cycle_enabled = True
def deactivate_rawrefcount_cycle(self):
- self.rrc_cycle_enabled = False
-
- def activate_rawrefcount_cycle(self):
- self.rrc_cycle_enabled = True
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ self.rrc_gc.rrc_cycle_enabled = False
def rawrefcount_create_link_pypy(self, gcobj, pyobject):
ll_assert(self.rrc_enabled, "rawrefcount.init not called")
- obj = llmemory.cast_ptr_to_adr(gcobj)
- objint = llmemory.cast_adr_to_int(obj, "symbolic")
- self._pyobj(pyobject).c_ob_pypy_link = objint
- #
- lst = self.rrc_p_list_young
- if self.is_in_nursery(obj):
- dct = self.rrc_p_dict_nurs
- else:
- dct = self.rrc_p_dict
- if not self.is_young_object(obj):
- lst = self.rrc_p_list_old
- lst.append(pyobject)
- dct.setitem(obj, pyobject)
+ self.rrc_gc.create_link_pypy(gcobj, pyobject)
def rawrefcount_create_link_pyobj(self, gcobj, pyobject):
ll_assert(self.rrc_enabled, "rawrefcount.init not called")
- obj = llmemory.cast_ptr_to_adr(gcobj)
- if self.is_young_object(obj):
- self.rrc_o_list_young.append(pyobject)
- else:
- self.rrc_o_list_old.append(pyobject)
- objint = llmemory.cast_adr_to_int(obj, "symbolic")
- self._pyobj(pyobject).c_ob_pypy_link = objint
- # there is no rrc_o_dict
+ self.rrc_gc.create_link_pyobj(gcobj, pyobject)
+
+ def rawrefcount_from_obj(self, gcobj):
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.from_obj(gcobj)
+
+ def rawrefcount_to_obj(self, pyobject):
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.to_obj(pyobject)
def rawrefcount_mark_deallocating(self, gcobj, pyobject):
ll_assert(self.rrc_enabled, "rawrefcount.init not called")
- obj = llmemory.cast_ptr_to_adr(gcobj) # should be a prebuilt obj
- objint = llmemory.cast_adr_to_int(obj, "symbolic")
- self._pyobj(pyobject).c_ob_pypy_link = objint
-
- def rawrefcount_from_obj(self, gcobj):
- obj = llmemory.cast_ptr_to_adr(gcobj)
- if self.is_in_nursery(obj):
- dct = self.rrc_p_dict_nurs
- else:
- dct = self.rrc_p_dict
- return dct.get(obj)
-
- def rawrefcount_to_obj(self, pyobject):
- obj = llmemory.cast_int_to_adr(self._pyobj(pyobject).c_ob_pypy_link)
- return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ self.rrc_gc.mark_deallocating(gcobj, pyobject)
def rawrefcount_next_dead(self):
- if self.rrc_dealloc_pending.non_empty():
- return self.rrc_dealloc_pending.pop()
- return llmemory.NULL
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.next_dead()
def rawrefcount_next_cyclic_isolate(self):
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_isolate_list):
- gchdr = self._rrc_gc_list_pop(self.rrc_pyobj_isolate_list)
- self._rrc_gc_list_add(self.rrc_pyobj_old_list, gchdr)
- return llmemory.cast_ptr_to_adr(self.rrc_gc_as_pyobj(gchdr))
- return llmemory.NULL
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.next_cyclic_isolate()
def rawrefcount_cyclic_garbage_head(self):
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_dead_list):
- return llmemory.cast_ptr_to_adr(
- self.rrc_gc_as_pyobj(self.rrc_pyobj_dead_list.c_gc_next))
- else:
- return llmemory.NULL
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.cyclic_garbage_head()
def rawrefcount_cyclic_garbage_remove(self):
- gchdr = self.rrc_pyobj_dead_list.c_gc_next
- # remove from old list
- next = gchdr.c_gc_next
- next.c_gc_prev = gchdr.c_gc_prev
- gchdr.c_gc_prev.c_gc_next = next
- # add to new list, may die later
- next = self.rrc_pyobj_list.c_gc_next
- self.rrc_pyobj_list.c_gc_next = gchdr
- gchdr.c_gc_prev = self.rrc_pyobj_list
- gchdr.c_gc_next = next
- next.c_gc_prev = gchdr
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ self.rrc_gc.cyclic_garbage_remove()
def rawrefcount_begin_garbage(self):
- # after this, no new objects should be marked with the GCFLAG_GARBAGE
- # flag
- self.rrc_state = self.RAWREFCOUNT_STATE_GARBAGE
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ self.rrc_gc.state = RawRefCountBaseGC.STATE_GARBAGE
def rawrefcount_end_garbage(self):
- # now there should not be any object left with a GCFLAG_GARBAGE flag
- # set
- self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ self.rrc_gc.state = RawRefCountBaseGC.STATE_DEFAULT
def rawrefcount_next_garbage_pypy(self):
- if self.rrc_garbage_to_trace.non_empty():
- # remove one object from the wavefront and move the wavefront
- obj = self.rrc_garbage_to_trace.pop()
- if self._rrc_garbage_visit(obj):
- return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
- else:
- return lltype.nullptr(llmemory.GCREF.TO)
- else:
- return lltype.nullptr(llmemory.GCREF.TO)
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.next_garbage_pypy()
def rawrefcount_next_garbage_pyobj(self):
- if self._rrc_gc_list_is_empty(self.rrc_pyobj_garbage_list):
- return llmemory.NULL
- else:
- # rrc_pyobj_garbage_list is not a real list, it just points to
- # the first (c_gc_next) and last (c_gc_prev) pyobject in the list
- # of live objects that are garbage, so just fix the references
- list = self.rrc_pyobj_garbage_list
- gchdr = list.c_gc_next
- if list.c_gc_prev == gchdr:
- list.c_gc_next = list # reached end of list, reset it
- else:
- list.c_gc_next = gchdr.c_gc_next # move pointer foward
- return llmemory.cast_ptr_to_adr(self.rrc_gc_as_pyobj(gchdr))
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ return self.rrc_gc.next_garbage_pyobj()
- def rrc_invoke_callback(self):
- if self.rrc_enabled and (self.rrc_dealloc_pending.non_empty() or
- not self._rrc_gc_list_is_empty(
- self.rrc_pyobj_isolate_list) or
- not self._rrc_gc_list_is_empty(
- self.rrc_pyobj_dead_list) or
- not self._rrc_gc_list_is_empty(
- self.rrc_pyobj_garbage_list)):
- self.rrc_dealloc_trigger_callback()
-
- def rrc_minor_collection_trace(self):
- length_estimate = self.rrc_p_dict_nurs.length()
- 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.singleaddr)
-
- def _rrc_minor_trace(self, pyobject, singleaddr):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
- #
- rc = self._pyobj(pyobject).c_ob_refcnt
- if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
- pass # the corresponding object may die
- else:
- # force the corresponding object to be alive
- intobj = self._pyobj(pyobject).c_ob_pypy_link
- singleaddr.address[0] = llmemory.cast_int_to_adr(intobj)
- 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")
- lst = self.rrc_p_list_young
- while lst.non_empty():
- self._rrc_minor_free(lst.pop(), self.rrc_p_list_old,
- self.rrc_p_dict)
- lst = self.rrc_o_list_young
- no_o_dict = self.null_address_dict()
- while lst.non_empty():
- self._rrc_minor_free(lst.pop(), self.rrc_o_list_old,
- no_o_dict)
-
- def _rrc_minor_free(self, pyobject, surviving_list, surviving_dict):
- intobj = self._pyobj(pyobject).c_ob_pypy_link
- obj = llmemory.cast_int_to_adr(intobj)
- if self.is_in_nursery(obj):
- if self.is_forwarded(obj):
- # Common case: survives and moves
- obj = self.get_forwarding_address(obj)
- intobj = llmemory.cast_adr_to_int(obj, "symbolic")
- self._pyobj(pyobject).c_ob_pypy_link = intobj
- surviving = True
- if surviving_dict:
- # Surviving nursery object: was originally in
- # rrc_p_dict_nurs and now must be put into rrc_p_dict
- surviving_dict.setitem(obj, pyobject)
- else:
- surviving = False
- elif (bool(self.young_rawmalloced_objects) and
- self.young_rawmalloced_objects.contains(obj)):
- # young weakref to a young raw-malloced object
- if self.header(obj).tid & GCFLAG_VISITED_RMY:
- surviving = True # survives, but does not move
- else:
- surviving = False
- if surviving_dict:
- # Dying young large object: was in rrc_p_dict,
- # must be deleted
- surviving_dict.setitem(obj, llmemory.NULL)
- else:
- ll_assert(False, "rrc_X_list_young contains non-young obj")
- return
- #
- if surviving:
- surviving_list.append(pyobject)
- else:
- self._rrc_free(pyobject)
-
- def _rrc_free(self, pyobject, major=False):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
- #
- rc = self._pyobj(pyobject).c_ob_refcnt
- if rc >= REFCNT_FROM_PYPY_LIGHT:
- rc -= REFCNT_FROM_PYPY_LIGHT
- if rc == 0:
- pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
- if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
- next = pygchdr.c_gc_next
- next.c_gc_prev = pygchdr.c_gc_prev
- pygchdr.c_gc_prev.c_gc_next = next
- lltype.free(self._pyobj(pyobject), flavor='raw')
- else:
- # can only occur if LIGHT is used in create_link_pyobj()
- self._pyobj(pyobject).c_ob_refcnt = rc
- self._pyobj(pyobject).c_ob_pypy_link = 0
- else:
- ll_assert(rc >= REFCNT_FROM_PYPY, "refcount underflow?")
- ll_assert(rc < int(REFCNT_FROM_PYPY_LIGHT * 0.99),
- "refcount underflow from REFCNT_FROM_PYPY_LIGHT?")
- rc -= REFCNT_FROM_PYPY
- self._pyobj(pyobject).c_ob_pypy_link = 0
- if rc == 0:
- #pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
- #if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
- # self._rrc_debug_check_list(pygchdr)
-
- # TODO?
- #pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject))
- #if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
- # next = pygchdr.c_gc_next
- # next.c_gc_prev = pygchdr.c_gc_prev
- # pygchdr.c_gc_prev.c_gc_next = next
-
- self.rrc_dealloc_pending.append(pyobject)
- # an object with refcnt == 0 cannot stay around waiting
- # for its deallocator to be called. Some code (lxml)
- # expects that tp_dealloc is called immediately when
- # the refcnt drops to 0. If it isn't, we get some
- # uncleared raw pointer that can still be used to access
- # the object; but (PyObject *)raw_pointer is then bogus
- # because after a Py_INCREF()/Py_DECREF() on it, its
- # tp_dealloc is also called!
- rc = 1
- self._pyobj(pyobject).c_ob_refcnt = rc
- _rrc_free._always_inline_ = True
-
- def rrc_major_collection_trace(self):
- if not self.rrc_cycle_enabled:
- self._rrc_debug_check_consistency(print_label="begin-mark")
-
- # First, untrack all tuples with only non-gc rrc objects and promote
- # all other tuples to the pyobj_list
- self._rrc_untrack_tuples()
-
- # Only trace and mark rawrefcounted object if we are not doing
- # something special, like building gc.garbage.
- if (self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT and
- self.rrc_cycle_enabled):
- merged_old_list = False
- # check objects with finalizers from last collection cycle
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
- merged_old_list = self._rrc_check_finalizer()
- # collect all rawrefcounted roots
- self._rrc_collect_roots(self.rrc_pyobj_list)
- if merged_old_list:
- # set all refcounts to zero for objects in dead list
- # (might have been incremented) by fix_refcnt
- gchdr = self.rrc_pyobj_dead_list.c_gc_next
- while gchdr <> self.rrc_pyobj_dead_list:
- gchdr.c_gc_refs = 0
- gchdr = gchdr.c_gc_next
- self._rrc_debug_check_consistency(print_label="roots-marked")
- # mark all objects reachable from rawrefcounted roots
- self._rrc_mark_rawrefcount()
- self._rrc_debug_check_consistency(print_label="before-fin")
- self.rrc_state = self.RAWREFCOUNT_STATE_MARKING
- if self._rrc_find_garbage(): # handle legacy finalizers
- self._rrc_mark_garbage()
- self._rrc_debug_check_consistency(print_label="end-legacy-fin")
- self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
- found_finalizer = self._rrc_find_finalizer() # modern finalizers
- if found_finalizer:
- self._rrc_gc_list_move(self.rrc_pyobj_old_list,
- self.rrc_pyobj_isolate_list)
- use_cylicrc = not found_finalizer
- self._rrc_debug_check_consistency(print_label="end-mark-cyclic")
- else:
- use_cylicrc = False # don't sweep any objects in cyclic isolates
-
- # now mark all pypy objects at the border, depending on the results
- debug_print("use_cylicrc", use_cylicrc)
- self.rrc_p_list_old.foreach(self._rrc_major_trace, use_cylicrc)
- self._rrc_debug_check_consistency(print_label="end-mark")
-
- # fix refcnt back
- self.rrc_refcnt_dict.foreach(self._rrc_fix_refcnt_back, None)
- self.rrc_refcnt_dict.delete()
- self.rrc_refcnt_dict = self.AddressDict()
-
- def _rrc_fix_refcnt_back(self, pyobject, link, ignore):
- pyobj = self._pyobj(pyobject)
- link_int = llmemory.cast_adr_to_int(link, "symbolic")
- pyobj.c_ob_refcnt = pyobj.c_ob_pypy_link
- pyobj.c_ob_pypy_link = link_int
-
- def _rrc_major_trace(self, pyobject, use_cylicrefcnt):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
- #
- pyobj = self._pyobj(pyobject)
- cyclic_rc = -42
- if use_cylicrefcnt:
- pygchdr = self.rrc_pyobj_as_gc(pyobj)
- if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
- if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
- rc = pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
- cyclic_rc = rc
- else:
- rc = pyobj.c_ob_refcnt
- else:
- rc = pyobj.c_ob_refcnt
- else:
- rc = pyobj.c_ob_refcnt
-
- if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT or rc == 0:
- pass # the corresponding object may die
- else:
- # force the corresponding object to be alive
- debug_print("pyobj stays alive", pyobj, "rc", rc, "cyclic_rc",
- cyclic_rc)
- obj = self.rrc_refcnt_dict.get(pyobject)
- self.objects_to_trace.append(obj)
- self.visit_all_objects()
-
- def _rrc_major_trace_nongc(self, pyobject, ignore):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
- #
- pyobj = self._pyobj(pyobject)
- pygchdr = self.rrc_pyobj_as_gc(pyobj)
- if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
- if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
- rc = 0
- else:
- rc = pyobj.c_ob_refcnt
- else:
- rc = pyobj.c_ob_refcnt
-
- if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT or rc == 0:
- pass # the corresponding object may die
- else:
- # force the corresponding object to be alive
- debug_print("pyobj stays alive", pyobj, "rc", rc)
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- obj = self.rrc_refcnt_dict.get(pyobject)
- self.objects_to_trace.append(obj)
- self.visit_all_objects()
-
- def rrc_major_collection_free(self):
- if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
- self._rrc_debug_check_consistency()
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
- self._rrc_clear_weakref_callbacks()
- self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
- self.rrc_pyobj_dead_list)
- self._rrc_debug_check_consistency(print_label="before-sweep")
-
- ll_assert(self.rrc_p_dict_nurs.length() == 0, "p_dict_nurs not empty
2")
- length_estimate = self.rrc_p_dict.length()
- self.rrc_p_dict.delete()
- self.rrc_p_dict = new_p_dict = self.AddressDict(length_estimate)
- new_p_list = self.AddressStack()
- while self.rrc_p_list_old.non_empty():
- self._rrc_major_free(self.rrc_p_list_old.pop(), new_p_list,
- new_p_dict)
- self.rrc_p_list_old.delete()
- self.rrc_p_list_old = new_p_list
- #
- new_o_list = self.AddressStack()
- no_o_dict = self.null_address_dict()
- while self.rrc_o_list_old.non_empty():
- self._rrc_major_free(self.rrc_o_list_old.pop(), new_o_list,
- no_o_dict)
- self.rrc_o_list_old.delete()
- self.rrc_o_list_old = new_o_list
-
- def _rrc_clear_weakref_callbacks(self):
- # Look for any weakrefs within the trash cycle and remove the callback.
- # This is only needed for weakrefs created from rawrefcounted objects
- # because weakrefs from gc-managed objects are going away anyway.
- return
- list = self.rrc_pyobj_old_list
- gchdr = list.c_gc_next
- while gchdr <> list:
- pyobj = self.rrc_gc_as_pyobj(gchdr)
- self._rrc_traverse_weakref(pyobj)
- gchdr = gchdr.c_gc_next
-
- def _rrc_untrack_tuples(self):
- gchdr = self.rrc_tuple_list.c_gc_next
- while gchdr <> self.rrc_tuple_list:
- gchdr_next = gchdr.c_gc_next
- pyobj = self.rrc_gc_as_pyobj(gchdr)
- result = self.rrc_tuple_maybe_untrack(pyobj)
- if result == 1: # contains gc objects -> promote to pyobj list
- next = gchdr.c_gc_next
- next.c_gc_prev = gchdr.c_gc_prev
- gchdr.c_gc_prev.c_gc_next = next
- self._rrc_gc_list_add(self.rrc_pyobj_list, gchdr)
- gchdr = gchdr_next
-
- def _rrc_visit_weakref(pyobj, self_ptr):
- from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
- #
- self_adr = rffi.cast(llmemory.Address, self_ptr)
- self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr)
- self._rrc_visit_weakref_action(pyobj, None)
- return rffi.cast(rffi.INT_real, 0)
-
- def _rrc_visit_weakref_action(self, pyobj, ignore):
- intobj = pyobj.c_ob_pypy_link
- if intobj <> 0:
- obj = llmemory.cast_int_to_adr(intobj)
- object = llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
- self.rrc_clear_weakref_callback(object)
-
- def _rrc_traverse_weakref(self, pyobj):
- from rpython.rlib.objectmodel import we_are_translated
- from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr,
- llhelper)
- #
- if we_are_translated():
- callback_ptr = llhelper(self.RAWREFCOUNT_VISIT,
- IncrementalMiniMarkGC._rrc_visit_weakref)
- self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self))
- self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr)
- else:
- self.rrc_tp_traverse(pyobj, self._rrc_visit_weakref_action, None)
-
- def _rrc_major_free(self, pyobject, surviving_list, surviving_dict):
- # The pyobject survives if the corresponding obj survives.
- # This is true if the obj has one of the following two flags:
- # * GCFLAG_VISITED: was seen during tracing
- # * GCFLAG_NO_HEAP_PTRS: immortal object never traced (so far)
- intobj = self._pyobj(pyobject).c_ob_pypy_link
- obj = llmemory.cast_int_to_adr(intobj)
- if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
- surviving_list.append(pyobject)
- if surviving_dict:
- surviving_dict.insertclean(obj, pyobject)
- else:
- self._rrc_free(pyobject, True)
-
- def _rrc_collect_roots(self, pygclist):
- # Initialize the cyclic refcount with the real refcount.
- self._rrc_collect_roots_init_list(pygclist)
-
- # Save the real refcount of objects at border
- self.rrc_p_list_old.foreach(self._rrc_obj_save_refcnt, None)
- self.rrc_o_list_old.foreach(self._rrc_obj_save_refcnt, None)
-
- # Subtract all internal refcounts from the cyclic refcount
- # of rawrefcounted objects
- self._rrc_collect_roots_subtract_internal(pygclist)
-
- # For all non-gc pyobjects which have a refcount > 0,
- # mark all reachable objects on the pypy side
- self.rrc_p_list_old.foreach(self._rrc_major_trace_nongc, None)
-
- # For every object in this set, if it is marked, add 1 as a real
- # refcount (p_list => pyobj stays alive if obj stays alive).
- self.rrc_p_list_old.foreach(self._rrc_obj_fix_refcnt, None)
- self.rrc_o_list_old.foreach(self._rrc_obj_fix_refcnt, None)
-
- # now all rawrefcounted roots or live border objects have a
- # refcount > 0
- self._rrc_debug_check_consistency(print_label="rc-initialized")
-
-
- def _rrc_collect_roots_init_list(self, pygclist):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
- pygchdr = pygclist.c_gc_next
- while pygchdr <> pygclist:
- refcnt = self.rrc_gc_as_pyobj(pygchdr).c_ob_refcnt
- if refcnt >= REFCNT_FROM_PYPY_LIGHT:
- refcnt -= REFCNT_FROM_PYPY_LIGHT
- elif refcnt >= REFCNT_FROM_PYPY:
- refcnt -= REFCNT_FROM_PYPY
- self._rrc_pyobj_gc_refcnt_set(pygchdr, refcnt)
- pygchdr = pygchdr.c_gc_next
-
- def _rrc_collect_roots_subtract_internal(self, pygclist):
- pygchdr = pygclist.c_gc_next
- while pygchdr <> pygclist:
- pyobj = self.rrc_gc_as_pyobj(pygchdr)
- self._rrc_traverse(pyobj, -1)
- pygchdr = pygchdr.c_gc_next
-
- def _rrc_pyobj_gc_refcnt_set(self, pygchdr, refcnt):
- pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED
- pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT
-
- def _rrc_obj_save_refcnt(self, pyobject, ignore):
- pyobj = self._pyobj(pyobject)
- link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link)
- self.rrc_refcnt_dict.setitem(pyobject, link)
- pyobj.c_ob_pypy_link = pyobj.c_ob_refcnt
-
- def _rrc_obj_fix_refcnt(self, pyobject, ignore):
- pyobj = self._pyobj(pyobject)
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- obj = self.rrc_refcnt_dict.get(pyobject)
- gchdr = self.rrc_pyobj_as_gc(pyobj)
- if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
- rc = gchdr.c_gc_refs
- refcnt = gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
- if rc == self.RAWREFCOUNT_REFS_UNTRACKED:
- debug_print("gc obj not tracked", gchdr, ": obj", obj,
- "cyclic-rc", rc)
- else:
- debug_print("gc obj tracked", gchdr, ": obj", obj, "real-rc",
- refcnt, "gc-next",
- gchdr.c_gc_next, "gc-prev", gchdr.c_gc_prev)
- if self.header(obj).tid & (GCFLAG_VISITED |
- GCFLAG_NO_HEAP_PTRS):
- refcnt += 1
- self._rrc_pyobj_gc_refcnt_set(gchdr, refcnt)
-
- def _rrc_mark_rawrefcount(self):
- if self._rrc_gc_list_is_empty(self.rrc_pyobj_list):
- self._rrc_gc_list_init(self.rrc_pyobj_old_list)
- else:
- self._rrc_gc_list_move(self.rrc_pyobj_list,
- self.rrc_pyobj_old_list)
- # as long as new objects with cyclic a refcount > 0 or alive border
- # objects are found, increment the refcount of all referenced objects
- # of those newly found objects
- found_alive = True
- pyobj_old = self.rrc_pyobj_list
- #
- while found_alive:
- found_alive = False
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
- next_old = gchdr.c_gc_next
- found_alive |= self._rrc_mark_rawrefcount_obj(gchdr, pyobj_old)
- gchdr = next_old
- #
- # now all rawrefcounted objects, which are alive, have a cyclic
- # refcount > 0 or are marked
-
- def _rrc_mark_rawrefcount_obj(self, gchdr, gchdr_move):
- alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
- pyobj = self.rrc_gc_as_pyobj(gchdr)
- obj = llmemory.NULL
- if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.rrc_refcnt_dict.get(pyobject)
- if not alive and self.header(obj).tid & (
- GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
- # add fake refcount, to mark it as live
- gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
- alive = True
- if alive:
- # remove from old list
- next = gchdr.c_gc_next
- next.c_gc_prev = gchdr.c_gc_prev
- gchdr.c_gc_prev.c_gc_next = next
- # add to new list (or not, if it is a tuple)
- self._rrc_gc_list_add(gchdr_move, gchdr)
- # increment refcounts
- self._rrc_traverse(pyobj, 1)
- # mark recursively, if it is a pypyobj
- if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- self.objects_to_trace.append(obj)
- self.visit_all_objects()
- return alive
-
- def _rrc_find_garbage(self):
- found_garbage = False
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
- next_old = gchdr.c_gc_next
- garbage = self.rrc_finalizer_type(gchdr) == \
- self.RAWREFCOUNT_FINALIZER_LEGACY
- if garbage:
- self._rrc_move_to_garbage(gchdr)
- found_garbage = True
- gchdr = next_old
- return found_garbage
-
- def _rrc_mark_garbage(self):
- found_garbage = True
- #
- while found_garbage:
- found_garbage = False
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
- next_old = gchdr.c_gc_next
- alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
- pyobj = self.rrc_gc_as_pyobj(gchdr)
- if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.rrc_refcnt_dict.get(pyobject)
- if not alive and self.header(obj).tid & (
- GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
- # add fake refcount, to mark it as live
- gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
- alive = True
- if alive:
- self._rrc_move_to_garbage(gchdr)
- found_garbage = True
- gchdr = next_old
-
- def _rrc_move_to_garbage(self, gchdr):
- pyobj = self.rrc_gc_as_pyobj(gchdr)
- # remove from old list
- next = gchdr.c_gc_next
- next.c_gc_prev = gchdr.c_gc_prev
- gchdr.c_gc_prev.c_gc_next = next
- # add to beginning of pyobj_list
- self._rrc_gc_list_add(self.rrc_pyobj_list, gchdr)
- # set as new beginning (and optionally end) of
- # pyobj_garbage_list (not a real list, just pointers to
- # begin and end)
- if self._rrc_gc_list_is_empty(self.rrc_pyobj_garbage_list):
- self.rrc_pyobj_garbage_list.c_gc_prev = gchdr
- self.rrc_pyobj_garbage_list.c_gc_next = gchdr
- # mark referenced objects alive (so objects in the old list
- # will be detected as garbage, as they should have a cyclic
- # refcount of zero or an unmarked linked pypy object)
- self._rrc_traverse(pyobj, 1)
- if pyobj.c_ob_pypy_link <> 0:
- #intobj = pyobj.c_ob_pypy_link
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.rrc_refcnt_dict.get(pyobject)
- #obj = llmemory.cast_int_to_adr(intobj)
- self.rrc_garbage_to_trace.append(obj)
- self.objects_to_trace.append(obj)
- self.visit_all_objects()
-
- def _rrc_collect_obj(self, obj, ignored):
- llop.debug_nonnull_pointer(lltype.Void, obj)
- self.rrc_garbage_to_trace.append(obj)
- _rrc_collect_obj._always_inline_ = True
-
- def _rrc_collect_ref_rec(self, root, ignored):
- self._rrc_collect_obj(root.address[0], None)
-
- def _rrc_garbage_visit(self, obj):
- # If GCFLAG_GARBAGE is set, remove the flag and trace the object
- hdr = self.header(obj)
- if not (hdr.tid & GCFLAG_GARBAGE):
- return False
- hdr.tid &= ~GCFLAG_GARBAGE
- if self.has_gcptr(llop.extract_ushort(llgroup.HALFWORD, hdr.tid)):
- self.trace(obj, self._rrc_collect_ref_rec, None)
- return True
-
- def _rrc_check_finalizer(self):
- # Check, if the cyclic isolate from the last collection cycle
- # is reachable from outside, after the finalizers have been
- # executed (and if all finalizers have been executed).
- found_alive = self._rrc_gc_list_is_empty(self.rrc_pyobj_isolate_list)
- if not found_alive:
- found_alive = self._rrc_find_finalizer()
- if not found_alive:
- self._rrc_collect_roots(self.rrc_pyobj_old_list)
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
- if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0:
- found_alive = True
- break
- gchdr = gchdr.c_gc_next
- if found_alive:
- self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
- self.rrc_pyobj_list)
- return False
- else:
- self._rrc_clear_weakref_callbacks()
- self._rrc_gc_list_merge(self.rrc_pyobj_old_list,
- self.rrc_pyobj_dead_list)
- return True
-
- def _rrc_find_finalizer(self):
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
- if self.rrc_finalizer_type(gchdr) == \
- self.RAWREFCOUNT_FINALIZER_MODERN:
- return True
- gchdr = gchdr.c_gc_next
- return False
-
- def _rrc_visit(pyobj, self_ptr):
- from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
- #
- self_adr = rffi.cast(llmemory.Address, self_ptr)
- self = cast_adr_to_nongc_instance(IncrementalMiniMarkGC, self_adr)
- self._rrc_visit_action(pyobj, None)
- return rffi.cast(rffi.INT_real, 0)
-
- def _rrc_visit_action(self, pyobj, ignore):
- pygchdr = self.rrc_pyobj_as_gc(pyobj)
- if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
- if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
- pygchdr.c_gc_refs += self.rrc_refcnt_add << \
- self.RAWREFCOUNT_REFS_SHIFT
- elif pyobj.c_ob_pypy_link != 0:
- pyobj.c_ob_refcnt += self.rrc_refcnt_add
- if self.rrc_refcnt_add > 0:
- #intobj = pyobj.c_ob_pypy_link
- #obj = llmemory.cast_int_to_adr(intobj)
- pyobject = llmemory.cast_ptr_to_adr(pyobj)
- obj = self.rrc_refcnt_dict.get(pyobject)
- self.objects_to_trace.append(obj)
- self.visit_all_objects()
-
- def _rrc_traverse(self, pyobj, refcnt_add):
- from rpython.rlib.objectmodel import we_are_translated
- from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr,
- llhelper)
- #
- self.rrc_refcnt_add = refcnt_add
- if we_are_translated():
- callback_ptr = llhelper(self.RAWREFCOUNT_VISIT,
- IncrementalMiniMarkGC._rrc_visit)
- self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self))
- self.rrc_tp_traverse(pyobj, callback_ptr, self_ptr)
- else:
- self.rrc_tp_traverse(pyobj, self._rrc_visit_action, None)
-
- def _rrc_gc_list_new(self):
- list = lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True)
- self._rrc_gc_list_init(list)
- return list
-
- def _rrc_gc_list_init(self, pygclist):
- pygclist.c_gc_next = pygclist
- pygclist.c_gc_prev = pygclist
-
- def _rrc_gc_list_add(self, pygclist, gchdr):
- next = pygclist.c_gc_next
- pygclist.c_gc_next = gchdr
- gchdr.c_gc_prev = pygclist
- gchdr.c_gc_next = next
- next.c_gc_prev = gchdr
-
- def _rrc_gc_list_pop(self, pygclist):
- ret = pygclist.c_gc_next
- pygclist.c_gc_next = ret.c_gc_next
- ret.c_gc_next.c_gc_prev = pygclist
- return ret
-
- def _rrc_gc_list_move(self, pygclist_source, pygclist_dest):
- pygclist_dest.c_gc_next = pygclist_source.c_gc_next
- pygclist_dest.c_gc_prev = pygclist_source.c_gc_prev
- pygclist_dest.c_gc_next.c_gc_prev = pygclist_dest
- pygclist_dest.c_gc_prev.c_gc_next = pygclist_dest
- pygclist_source.c_gc_next = pygclist_source
- pygclist_source.c_gc_prev = pygclist_source
-
- def _rrc_gc_list_merge(self, pygclist_source, pygclist_dest):
- next = pygclist_dest.c_gc_next
- next_old = pygclist_source.c_gc_next
- prev_old = pygclist_source.c_gc_prev
- pygclist_dest.c_gc_next = next_old
- next_old.c_gc_prev = pygclist_dest
- prev_old.c_gc_next = next
- next.c_gc_prev = prev_old
- pygclist_source.c_gc_next = pygclist_source
- pygclist_source.c_gc_prev = pygclist_source
-
- def _rrc_gc_list_is_empty(self, pygclist):
- return pygclist.c_gc_next == pygclist
-
- def _rrc_debug_check_consistency(self, print_label=None):
- if self.DEBUG:
- should_print = print_label is not None
- if should_print:
- debug_start("rrc-lists " + print_label)
- self._rrc_debug_check_list(self.rrc_pyobj_list,
- should_print, "rrc_pyobj_list")
- self._rrc_debug_check_list(self.rrc_tuple_list,
- should_print, "rrc_tuple_list")
- self._rrc_debug_check_list(self.rrc_pyobj_old_list,
- should_print, "rrc_pyobj_old_list")
- self._rrc_debug_check_list(self.rrc_pyobj_dead_list,
- should_print, "rrc_pyobj_dead_list")
- self._rrc_debug_check_list(self.rrc_pyobj_isolate_list,
- should_print, "rrc_pyobj_isolate_list")
- # rrc_pyobj_garbage_list is not a real list, it just marks the
- # first and the last object in rrc_pyobj_list, which are garbage
-
- if should_print:
- debug_stop("rrc-lists " + print_label)
-
- def _rrc_debug_check_list(self, list, should_print, print_label):
- if should_print:
- debug_start(print_label)
- gchdr = list.c_gc_next
- prev = list
- while gchdr <> list:
- if should_print:
- pyobj = self.rrc_gc_as_pyobj(gchdr)
- intobj = pyobj.c_ob_pypy_link
- debug_print("item", gchdr, ": pyobj", pyobj,
- "cyclic refcnt",
- gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT,
- "refcnt", pyobj.c_ob_refcnt,
- "link", intobj)
- #if intobj: TODO fix
- # obj = llmemory.cast_int_to_adr(intobj)
- # marked = self.header(obj).tid & \
- # (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS)
- # debug_print(" linked obj", obj, ": marked", marked)
-
- ll_assert(gchdr.c_gc_next != lltype.nullptr(self.PYOBJ_GC_HDR),
- "gc_next is null")
- ll_assert(gchdr.c_gc_prev == prev, "gc_prev is inconsistent")
- prev = gchdr
- gchdr = gchdr.c_gc_next
- ll_assert(list.c_gc_prev == prev, "gc_prev is inconsistent")
- if should_print:
- debug_stop(print_label)
\ No newline at end of file
+ def check_no_more_rawrefcount_state(self):
+ ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+ self.rrc_gc.check_no_more_rawrefcount_state()
diff --git a/rpython/memory/gc/__init__.py b/rpython/memory/gc/rrc/__init__.py
copy from rpython/memory/gc/__init__.py
copy to rpython/memory/gc/rrc/__init__.py
diff --git a/rpython/memory/gc/rrc/base.py b/rpython/memory/gc/rrc/base.py
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/rrc/base.py
@@ -0,0 +1,882 @@
+from rpython.rtyper.lltypesystem import lltype, llmemory, llgroup, rffi
+from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop
+
+def choose_rrc_gc_from_config(config):
+ if config.translation.rrcgc:
+ classes = {"mark": "mark.RawRefCountMarkGC",
+ "incmark": "incmark.RawRefCountIncMarkGC",
+ }
+ try:
+ modulename, classname = classes[config.translation.rrcgc].split(
+ '.')
+ except KeyError:
+ raise ValueError("unknown value for translation.rrcgc: %r" % (
+ config.translation.rrcgc,))
+ module = __import__("rpython.memory.gc.rrc." + modulename,
+ globals(), locals(), [classname])
+ GCClass = getattr(module, classname)
+ return GCClass
+ else:
+ return None
+
+class RawRefCountBaseGC(object):
+ # Default state, no rawrefcount specific code is executed during normal
marking.
+ STATE_DEFAULT = 0
+
+ # Here cyclic garbage only reachable from legacy finalizers is marked.
+ STATE_MARKING = 1
+
+ # The state in which cyclic garbage with legacy finalizers is traced.
+ # Do not mark objects during this state, because we remove the flag
+ # during tracing and we do not want to trace those objects again. Also
+ # during this phase no new objects can be marked, as we are only building
+ # the list of cyclic garbage.
+ STATE_GARBAGE = 2
+
+ _ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
+ PYOBJ_SNAPSHOT_OBJ = lltype.Struct('PyObject_Snapshot',
+ ('pyobj', llmemory.Address),
+ ('refcnt', lltype.Signed),
+ ('refcnt_internal', lltype.Signed),
+ ('refs_index', lltype.Signed),
+ ('refs_len', lltype.Signed))
+ PYOBJ_SNAPSHOT = lltype.Array(PYOBJ_SNAPSHOT_OBJ,
+ hints={'nolength': True})
+ PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
+ ('c_ob_refcnt', lltype.Signed),
+ ('c_ob_pypy_link', lltype.Signed))
+ PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
+ RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
+ RAWREFCOUNT_VISIT = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR, rffi.VOIDP],
+ rffi.INT_real))
+ RAWREFCOUNT_TRAVERSE = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR,
+ RAWREFCOUNT_VISIT,
+ rffi.VOIDP],
+ lltype.Void))
+ PYOBJ_GC_HDR_PTR = lltype.Ptr(lltype.ForwardReference())
+ PYOBJ_GC_HDR = lltype.Struct('PyGC_Head',
+ ('c_gc_next', PYOBJ_GC_HDR_PTR),
+ ('c_gc_prev', PYOBJ_GC_HDR_PTR),
+ ('c_gc_refs', lltype.Signed))
+ PYOBJ_GC_HDR_PTR.TO.become(PYOBJ_GC_HDR)
+ RAWREFCOUNT_GC_AS_PYOBJ = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
+ PYOBJ_HDR_PTR))
+ RAWREFCOUNT_PYOBJ_AS_GC = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR],
+ PYOBJ_GC_HDR_PTR))
+ RAWREFCOUNT_FINALIZER_TYPE = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR],
+ lltype.Signed))
+ RAWREFCOUNT_CLEAR_WR_TYPE = lltype.Ptr(lltype.FuncType([llmemory.GCREF],
+ lltype.Void))
+ RAWREFCOUNT_MAYBE_UNTRACK_TUPLE = \
+ lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR], lltype.Signed))
+ RAWREFCOUNT_FINALIZER_NONE = 0
+ RAWREFCOUNT_FINALIZER_MODERN = 1
+ RAWREFCOUNT_FINALIZER_LEGACY = 2
+ RAWREFCOUNT_REFS_SHIFT = 1
+ RAWREFCOUNT_REFS_MASK_FINALIZED = 1
+ RAWREFCOUNT_REFS_UNTRACKED = -2 << RAWREFCOUNT_REFS_SHIFT
+
+ def _pyobj(self, pyobjaddr):
+ return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
+ def _pygchdr(self, pygchdraddr):
+ return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR)
+
+ def init(self, gc, gc_flags, dealloc_trigger_callback, tp_traverse,
+ pyobj_list, tuple_list, gc_as_pyobj, pyobj_as_gc, finalizer_type,
+ clear_weakref_callback, tuple_maybe_untrack):
+ # see pypy/doc/discussion/rawrefcount.rst
+ self.gc = gc
+ (self.GCFLAG_VISITED_RMY, self.GCFLAG_VISITED,
+ self.GCFLAG_NO_HEAP_PTRS, self.GCFLAG_GARBAGE) = gc_flags
+ self.p_list_young = self.gc.AddressStack()
+ self.p_list_old = self.gc.AddressStack()
+ self.o_list_young = self.gc.AddressStack()
+ self.o_list_old = self.gc.AddressStack()
+ self.p_dict = self.gc.AddressDict() # non-nursery keys only
+ self.p_dict_nurs = self.gc.AddressDict() # nursery keys only
+ self.dealloc_trigger_callback = dealloc_trigger_callback
+ self.dealloc_pending = self.gc.AddressStack()
+ self.refcnt_dict = self.gc.AddressDict()
+ self.tp_traverse = tp_traverse
+ self.pyobj_list = self._pygchdr(pyobj_list)
+ self.tuple_list = self._pygchdr(tuple_list)
+ self.pyobj_old_list = self._gc_list_new()
+ self.pyobj_isolate_list = self._gc_list_new()
+ self.pyobj_dead_list = self._gc_list_new()
+ self.pyobj_garbage_list = self._gc_list_new()
+ self.garbage_to_trace = self.gc.AddressStack()
+ self.gc_as_pyobj = gc_as_pyobj
+ self.pyobj_as_gc = pyobj_as_gc
+ self.finalizer_type = finalizer_type
+ self.clear_weakref_callback = clear_weakref_callback
+ self.tuple_maybe_untrack = tuple_maybe_untrack
+ self.state = self.STATE_DEFAULT
+ self.cycle_enabled = True
+
+ def create_link_pypy(self, gcobj, pyobject):
+ obj = llmemory.cast_ptr_to_adr(gcobj)
+ objint = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).c_ob_pypy_link = objint
+ #
+ lst = self.p_list_young
+ if self.gc.is_in_nursery(obj):
+ dct = self.p_dict_nurs
+ else:
+ dct = self.p_dict
+ if not self.gc.is_young_object(obj):
+ lst = self.p_list_old
+ lst.append(pyobject)
+ dct.setitem(obj, pyobject)
+
+ def create_link_pyobj(self, gcobj, pyobject):
+ obj = llmemory.cast_ptr_to_adr(gcobj)
+ if self.gc.is_young_object(obj):
+ self.o_list_young.append(pyobject)
+ else:
+ self.o_list_old.append(pyobject)
+ objint = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).c_ob_pypy_link = objint
+ # there is no o_dict
+
+ def from_obj(self, gcobj):
+ obj = llmemory.cast_ptr_to_adr(gcobj)
+ if self.gc.is_in_nursery(obj):
+ dct = self.p_dict_nurs
+ else:
+ dct = self.p_dict
+ return dct.get(obj)
+
+ def to_obj(self, pyobject):
+ obj = llmemory.cast_int_to_adr(self._pyobj(pyobject).c_ob_pypy_link)
+ return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+
+ def mark_deallocating(self, gcobj, pyobject):
+ obj = llmemory.cast_ptr_to_adr(gcobj) # should be a prebuilt obj
+ objint = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).c_ob_pypy_link = objint
+
+ def invoke_callback(self):
+ if (self.dealloc_pending.non_empty() or
+ not self._gc_list_is_empty(self.pyobj_isolate_list) or
+ not self._gc_list_is_empty(self.pyobj_dead_list) or
+ not self._gc_list_is_empty(self.pyobj_garbage_list)):
+ self.dealloc_trigger_callback()
+
+ def next_dead(self):
+ if self.dealloc_pending.non_empty():
+ return self.dealloc_pending.pop()
+ return llmemory.NULL
+
+ def next_cyclic_isolate(self):
+ if not self._gc_list_is_empty(self.pyobj_isolate_list):
+ gchdr = self._gc_list_pop(self.pyobj_isolate_list)
+ self._gc_list_add(self.pyobj_old_list, gchdr)
+ return llmemory.cast_ptr_to_adr(self.gc_as_pyobj(gchdr))
+ return llmemory.NULL
+
+ def cyclic_garbage_head(self):
+ if not self._gc_list_is_empty(self.pyobj_dead_list):
+ return llmemory.cast_ptr_to_adr(
+ self.gc_as_pyobj(self.pyobj_dead_list.c_gc_next))
+ else:
+ return llmemory.NULL
+
+ def cyclic_garbage_remove(self):
+ gchdr = self.pyobj_dead_list.c_gc_next
+ # remove from old list
+ next = gchdr.c_gc_next
+ next.c_gc_prev = gchdr.c_gc_prev
+ gchdr.c_gc_prev.c_gc_next = next
+ # add to new list, may die later
+ next = self.pyobj_list.c_gc_next
+ self.pyobj_list.c_gc_next = gchdr
+ gchdr.c_gc_prev = self.pyobj_list
+ gchdr.c_gc_next = next
+ next.c_gc_prev = gchdr
+
+ def next_garbage_pypy(self):
+ if self.garbage_to_trace.non_empty():
+ # remove one object from the wavefront and move the wavefront
+ obj = self.garbage_to_trace.pop()
+ if self._garbage_visit(obj):
+ return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ else:
+ return lltype.nullptr(llmemory.GCREF.TO)
+ else:
+ return lltype.nullptr(llmemory.GCREF.TO)
+
+ def next_garbage_pyobj(self):
+ if self._gc_list_is_empty(self.pyobj_garbage_list):
+ return llmemory.NULL
+ else:
+ # pyobj_garbage_list is not a real list, it just points to
+ # the first (c_gc_next) and last (c_gc_prev) pyobject in the list
+ # of live objects that are garbage, so just fix the references
+ list = self.pyobj_garbage_list
+ gchdr = list.c_gc_next
+ if list.c_gc_prev == gchdr:
+ list.c_gc_next = list # reached end of list, reset it
+ else:
+ list.c_gc_next = gchdr.c_gc_next # move pointer foward
+ return llmemory.cast_ptr_to_adr(self.gc_as_pyobj(gchdr))
+
+ # --- Tracing ---
+
+ def minor_trace(self):
+ length_estimate = self.p_dict_nurs.length()
+ self.p_dict_nurs.delete()
+ self.p_dict_nurs = self.gc.AddressDict(length_estimate)
+ self.p_list_young.foreach(self._minor_trace, self.gc.singleaddr)
+
+ def _minor_trace(self, pyobject, singleaddr):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ rc = self._pyobj(pyobject).c_ob_refcnt
+ if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
+ pass # the corresponding object may die
+ else:
+ # force the corresponding object to be alive
+ intobj = self._pyobj(pyobject).c_ob_pypy_link
+ singleaddr.address[0] = llmemory.cast_int_to_adr(intobj)
+ self.gc._trace_drag_out1(singleaddr)
+
+ def minor_collection_free(self):
+ ll_assert(self.p_dict_nurs.length() == 0,
+ "p_dict_nurs not empty 1")
+ lst = self.p_list_young
+ while lst.non_empty():
+ self._minor_free(lst.pop(), self.p_list_old, self.p_dict)
+ lst = self.o_list_young
+ no_o_dict = self.gc.null_address_dict()
+ while lst.non_empty():
+ self._minor_free(lst.pop(), self.o_list_old, no_o_dict)
+
+ def _minor_free(self, pyobject, surviving_list, surviving_dict):
+ intobj = self._pyobj(pyobject).c_ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
+ if self.gc.is_in_nursery(obj):
+ if self.gc.is_forwarded(obj):
+ # Common case: survives and moves
+ obj = self.gc.get_forwarding_address(obj)
+ intobj = llmemory.cast_adr_to_int(obj, "symbolic")
+ self._pyobj(pyobject).c_ob_pypy_link = intobj
+ surviving = True
+ if surviving_dict:
+ # Surviving nursery object: was originally in
+ # p_dict_nurs and now must be put into p_dict
+ surviving_dict.setitem(obj, pyobject)
+ else:
+ surviving = False
+ elif (bool(self.gc.young_rawmalloced_objects) and
+ self.gc.young_rawmalloced_objects.contains(obj)):
+ # young weakref to a young raw-malloced object
+ if self.gc.header(obj).tid & self.GCFLAG_VISITED_RMY:
+ surviving = True # survives, but does not move
+ else:
+ surviving = False
+ if surviving_dict:
+ # Dying young large object: was in p_dict,
+ # must be deleted
+ surviving_dict.setitem(obj, llmemory.NULL)
+ else:
+ ll_assert(False, "X_list_young contains non-young obj")
+ return
+ #
+ if surviving:
+ surviving_list.append(pyobject)
+ else:
+ self._free(pyobject)
+
+ def _free(self, pyobject, major=False):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ rc = self._pyobj(pyobject).c_ob_refcnt
+ if rc >= REFCNT_FROM_PYPY_LIGHT:
+ rc -= REFCNT_FROM_PYPY_LIGHT
+ if rc == 0:
+ pygchdr = self.pyobj_as_gc(self._pyobj(pyobject))
+ if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
+ next = pygchdr.c_gc_next
+ next.c_gc_prev = pygchdr.c_gc_prev
+ pygchdr.c_gc_prev.c_gc_next = next
+ lltype.free(self._pyobj(pyobject), flavor='raw')
+ else:
+ # can only occur if LIGHT is used in create_link_pyobj()
+ self._pyobj(pyobject).c_ob_refcnt = rc
+ self._pyobj(pyobject).c_ob_pypy_link = 0
+ else:
+ ll_assert(rc >= REFCNT_FROM_PYPY, "refcount underflow?")
+ ll_assert(rc < int(REFCNT_FROM_PYPY_LIGHT * 0.99),
+ "refcount underflow from REFCNT_FROM_PYPY_LIGHT?")
+ rc -= REFCNT_FROM_PYPY
+ self._pyobj(pyobject).c_ob_pypy_link = 0
+ if rc == 0:
+ self.dealloc_pending.append(pyobject)
+ # an object with refcnt == 0 cannot stay around waiting
+ # for its deallocator to be called. Some code (lxml)
+ # expects that tp_dealloc is called immediately when
+ # the refcnt drops to 0. If it isn't, we get some
+ # uncleared raw pointer that can still be used to access
+ # the object; but (PyObject *)raw_pointer is then bogus
+ # because after a Py_INCREF()/Py_DECREF() on it, its
+ # tp_dealloc is also called!
+ rc = 1
+ self._pyobj(pyobject).c_ob_refcnt = rc
+ _free._always_inline_ = True
+
+ def major_collection_trace(self):
+ if not self.cycle_enabled:
+ self._debug_check_consistency(print_label="begin-mark")
+
+ # First, untrack all tuples with only non-gc rrc objects and promote
+ # all other tuples to the pyobj_list
+ self._untrack_tuples()
+
+ # Only trace and mark rawrefcounted object if we are not doing
+ # something special, like building gc.garbage.
+ if (self.state == self.STATE_DEFAULT and self.cycle_enabled):
+ merged_old_list = False
+ # check objects with finalizers from last collection cycle
+ if not self._gc_list_is_empty(self.pyobj_old_list):
+ merged_old_list = self._check_finalizer()
+ # collect all rawrefcounted roots
+ self._collect_roots(self.pyobj_list) # TODO: from snapshot
+ if merged_old_list:
+ # set all refcounts to zero for objects in dead list
+ # (might have been incremented) by fix_refcnt
+ gchdr = self.pyobj_dead_list.c_gc_next
+ while gchdr <> self.pyobj_dead_list:
+ gchdr.c_gc_refs = 0
+ gchdr = gchdr.c_gc_next
+ self._debug_check_consistency(print_label="roots-marked")
+ # mark all objects reachable from rawrefcounted roots
+ self._mark_rawrefcount() # TODO: from snapshot
+ self._debug_check_consistency(print_label="before-fin")
+ self.state = self.STATE_MARKING
+ if self._find_garbage(): # handle legacy finalizers # TODO: from
snapshot
+ self._mark_garbage() # TODO: from snapshot
+ self._debug_check_consistency(print_label="end-legacy-fin")
+ self.state = self.STATE_DEFAULT
+ found_finalizer = self._find_finalizer() # modern finalizers #
TODO: from snapshot
+ if found_finalizer:
+ self._gc_list_move(self.pyobj_old_list,
+ self.pyobj_isolate_list)
+ use_cylicrc = not found_finalizer
+ self._debug_check_consistency(print_label="end-mark-cyclic")
+ else:
+ use_cylicrc = False # don't sweep any objects in cyclic isolates
+
+ # now mark all pypy objects at the border, depending on the results
+ debug_print("use_cylicrc", use_cylicrc)
+ self.p_list_old.foreach(self._major_trace, use_cylicrc)
+ self._debug_check_consistency(print_label="end-mark")
+
+ # fix refcnt back
+ self.refcnt_dict.foreach(self._fix_refcnt_back, None) # TODO: from
snapshot?
+ self.refcnt_dict.delete()
+ self.refcnt_dict = self.gc.AddressDict()
+
+ def _fix_refcnt_back(self, pyobject, link, ignore):
+ pyobj = self._pyobj(pyobject)
+ link_int = llmemory.cast_adr_to_int(link, "symbolic")
+ pyobj.c_ob_refcnt = pyobj.c_ob_pypy_link
+ pyobj.c_ob_pypy_link = link_int
+
+ def _major_trace(self, pyobject, use_cylicrefcnt):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ pyobj = self._pyobj(pyobject)
+ cyclic_rc = -42
+ if use_cylicrefcnt:
+ pygchdr = self.pyobj_as_gc(pyobj)
+ if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
+ if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
+ rc = pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
+ cyclic_rc = rc
+ else:
+ rc = pyobj.c_ob_refcnt
+ else:
+ rc = pyobj.c_ob_refcnt
+ else:
+ rc = pyobj.c_ob_refcnt
+
+ if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT or rc == 0:
+ pass # the corresponding object may die
+ else:
+ # force the corresponding object to be alive
+ debug_print("pyobj stays alive", pyobj, "rc", rc, "cyclic_rc",
+ cyclic_rc)
+ obj = self.refcnt_dict.get(pyobject)
+ self.gc.objects_to_trace.append(obj)
+ self.gc.visit_all_objects()
+
+ def _major_trace_nongc(self, pyobject, ignore):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ #
+ pyobj = self._pyobj(pyobject)
+ pygchdr = self.pyobj_as_gc(pyobj)
+ if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR):
+ if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
+ rc = 0
+ else:
+ rc = pyobj.c_ob_refcnt
+ else:
+ rc = pyobj.c_ob_refcnt
+
+ if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT or rc == 0:
+ pass # the corresponding object may die
+ else:
+ # force the corresponding object to be alive
+ debug_print("pyobj stays alive", pyobj, "rc", rc)
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ obj = self.refcnt_dict.get(pyobject)
+ self.gc.objects_to_trace.append(obj)
+ self.gc.visit_all_objects()
+
+ def major_collection_free(self):
+ if self.state == self.STATE_DEFAULT:
+ self._debug_check_consistency()
+ if not self._gc_list_is_empty(self.pyobj_old_list):
+ self._gc_list_merge(self.pyobj_old_list, self.pyobj_dead_list)
+ self._debug_check_consistency(print_label="before-sweep")
+
+ ll_assert(self.p_dict_nurs.length() == 0, "p_dict_nurs not empty 2")
+ length_estimate = self.p_dict.length()
+ self.p_dict.delete()
+ self.p_dict = new_p_dict = self.gc.AddressDict(length_estimate)
+ new_p_list = self.gc.AddressStack()
+ while self.p_list_old.non_empty():
+ self._major_free(self.p_list_old.pop(), new_p_list,
+ new_p_dict)
+ self.p_list_old.delete()
+ self.p_list_old = new_p_list
+ #
+ new_o_list = self.gc.AddressStack()
+ no_o_dict = self.gc.null_address_dict()
+ while self.o_list_old.non_empty():
+ self._major_free(self.o_list_old.pop(), new_o_list, no_o_dict)
+ self.o_list_old.delete()
+ self.o_list_old = new_o_list
+
+ def _major_free(self, pyobject, surviving_list, surviving_dict):
+ # The pyobject survives if the corresponding obj survives.
+ # This is true if the obj has one of the following two flags:
+ # * GCFLAG_VISITED: was seen during tracing
+ # * GCFLAG_NO_HEAP_PTRS: immortal object never traced (so far)
+ intobj = self._pyobj(pyobject).c_ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
+ if self.gc.header(obj).tid & \
+ (self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS):
+ surviving_list.append(pyobject)
+ if surviving_dict:
+ surviving_dict.insertclean(obj, pyobject)
+ else:
+ self._free(pyobject, True)
+
+
+ def _untrack_tuples(self):
+ gchdr = self.tuple_list.c_gc_next
+ while gchdr <> self.tuple_list:
+ gchdr_next = gchdr.c_gc_next
+ pyobj = self.gc_as_pyobj(gchdr)
+ result = self.tuple_maybe_untrack(pyobj)
+ if result == 1: # contains gc objects -> promote to pyobj list
+ next = gchdr.c_gc_next
+ next.c_gc_prev = gchdr.c_gc_prev
+ gchdr.c_gc_prev.c_gc_next = next
+ self._gc_list_add(self.pyobj_list, gchdr)
+ gchdr = gchdr_next
+
+ def _collect_roots(self, pygclist):
+ # Initialize the cyclic refcount with the real refcount.
+ self._collect_roots_init_list(pygclist)
+
+ # Save the real refcount of objects at border
+ self.p_list_old.foreach(self._obj_save_refcnt, None)
+ self.o_list_old.foreach(self._obj_save_refcnt, None)
+
+ # Subtract all internal refcounts from the cyclic refcount
+ # of rawrefcounted objects
+ self._collect_roots_subtract_internal(pygclist)
+
+ # For all non-gc pyobjects which have a refcount > 0,
+ # mark all reachable objects on the pypy side
+ self.p_list_old.foreach(self._major_trace_nongc, None)
+
+ # For every object in this set, if it is marked, add 1 as a real
+ # refcount (p_list => pyobj stays alive if obj stays alive).
+ self.p_list_old.foreach(self._obj_fix_refcnt, None)
+ self.o_list_old.foreach(self._obj_fix_refcnt, None)
+
+ # now all rawrefcounted roots or live border objects have a
+ # refcount > 0
+ self._debug_check_consistency(print_label="rc-initialized")
+
+ def _collect_roots_init_list(self, pygclist):
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+ from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ pygchdr = pygclist.c_gc_next
+ while pygchdr <> pygclist:
+ refcnt = self.gc_as_pyobj(pygchdr).c_ob_refcnt
+ if refcnt >= REFCNT_FROM_PYPY_LIGHT:
+ refcnt -= REFCNT_FROM_PYPY_LIGHT
+ elif refcnt >= REFCNT_FROM_PYPY:
+ refcnt -= REFCNT_FROM_PYPY
+ self._pyobj_gc_refcnt_set(pygchdr, refcnt)
+ pygchdr = pygchdr.c_gc_next
+
+ def _collect_roots_subtract_internal(self, pygclist):
+ pygchdr = pygclist.c_gc_next
+ while pygchdr <> pygclist:
+ pyobj = self.gc_as_pyobj(pygchdr)
+ self._traverse(pyobj, -1)
+ pygchdr = pygchdr.c_gc_next
+
+ def _pyobj_gc_refcnt_set(self, pygchdr, refcnt):
+ pygchdr.c_gc_refs &= self.RAWREFCOUNT_REFS_MASK_FINALIZED
+ pygchdr.c_gc_refs |= refcnt << self.RAWREFCOUNT_REFS_SHIFT
+
+ def _obj_save_refcnt(self, pyobject, ignore):
+ pyobj = self._pyobj(pyobject)
+ link = llmemory.cast_int_to_adr(pyobj.c_ob_pypy_link)
+ self.refcnt_dict.setitem(pyobject, link)
+ pyobj.c_ob_pypy_link = pyobj.c_ob_refcnt
+
+ def _obj_fix_refcnt(self, pyobject, ignore):
+ pyobj = self._pyobj(pyobject)
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ obj = self.refcnt_dict.get(pyobject)
+ gchdr = self.pyobj_as_gc(pyobj)
+ if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
+ rc = gchdr.c_gc_refs
+ refcnt = gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT
+ if rc == self.RAWREFCOUNT_REFS_UNTRACKED:
+ debug_print("gc obj not tracked", gchdr, ": obj", obj,
+ "cyclic-rc", rc)
+ else:
+ debug_print("gc obj tracked", gchdr, ": obj", obj, "real-rc",
+ refcnt, "gc-next",
+ gchdr.c_gc_next, "gc-prev", gchdr.c_gc_prev)
+ if self.gc.header(obj).tid & (self.GCFLAG_VISITED |
+ self.GCFLAG_NO_HEAP_PTRS):
+ refcnt += 1
+ self._pyobj_gc_refcnt_set(gchdr, refcnt)
+
+ def _mark_rawrefcount(self):
+ if self._gc_list_is_empty(self.pyobj_list):
+ self._gc_list_init(self.pyobj_old_list)
+ else:
+ self._gc_list_move(self.pyobj_list, self.pyobj_old_list)
+ # as long as new objects with cyclic a refcount > 0 or alive border
+ # objects are found, increment the refcount of all referenced objects
+ # of those newly found objects
+ found_alive = True
+ pyobj_old = self.pyobj_list
+ #
+ while found_alive: # TODO: working set to improve performance?
+ found_alive = False
+ gchdr = self.pyobj_old_list.c_gc_next
+ while gchdr <> self.pyobj_old_list:
+ next_old = gchdr.c_gc_next
+ found_alive |= self._mark_rawrefcount_obj(gchdr, pyobj_old)
+ gchdr = next_old
+ #
+ # now all rawrefcounted objects, which are alive, have a cyclic
+ # refcount > 0 or are marked
+
+ def _mark_rawrefcount_obj(self, gchdr, gchdr_move):
+ alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
+ pyobj = self.gc_as_pyobj(gchdr)
+ obj = llmemory.NULL
+ if pyobj.c_ob_pypy_link <> 0:
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ if not alive and self.gc.header(obj).tid & (
+ self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS):
+ # add fake refcount, to mark it as live
+ gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
+ alive = True
+ if alive:
+ # remove from old list
+ next = gchdr.c_gc_next
+ next.c_gc_prev = gchdr.c_gc_prev
+ gchdr.c_gc_prev.c_gc_next = next
+ # add to new list (or not, if it is a tuple)
+ self._gc_list_add(gchdr_move, gchdr)
+ # increment refcounts
+ self._traverse(pyobj, 1)
+ # mark recursively, if it is a pypyobj
+ if pyobj.c_ob_pypy_link <> 0:
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ self.gc.objects_to_trace.append(obj)
+ self.gc.visit_all_objects()
+ return alive
+
+ def _find_garbage(self):
+ found_garbage = False
+ gchdr = self.pyobj_old_list.c_gc_next
+ while gchdr <> self.pyobj_old_list:
+ next_old = gchdr.c_gc_next
+ garbage = self.finalizer_type(gchdr) == \
+ self.RAWREFCOUNT_FINALIZER_LEGACY
+ if garbage:
+ self._move_to_garbage(gchdr)
+ found_garbage = True
+ gchdr = next_old
+ return found_garbage
+
+ def _mark_garbage(self):
+ found_garbage = True
+ #
+ while found_garbage:
+ found_garbage = False
+ gchdr = self.pyobj_old_list.c_gc_next
+ while gchdr <> self.pyobj_old_list:
+ next_old = gchdr.c_gc_next
+ alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0
+ pyobj = self.gc_as_pyobj(gchdr)
+ if pyobj.c_ob_pypy_link <> 0:
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ if not alive and self.gc.header(obj).tid & (
+ self.GCFLAG_VISITED | self.GCFLAG_NO_HEAP_PTRS):
+ # add fake refcount, to mark it as live
+ gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT
+ alive = True
+ if alive:
+ self._move_to_garbage(gchdr)
+ found_garbage = True
+ gchdr = next_old
+
+ def _move_to_garbage(self, gchdr):
+ pyobj = self.gc_as_pyobj(gchdr)
+ # remove from old list
+ next = gchdr.c_gc_next
+ next.c_gc_prev = gchdr.c_gc_prev
+ gchdr.c_gc_prev.c_gc_next = next
+ # add to beginning of pyobj_list
+ self._gc_list_add(self.pyobj_list, gchdr)
+ # set as new beginning (and optionally end) of
+ # pyobj_garbage_list (not a real list, just pointers to
+ # begin and end)
+ if self._gc_list_is_empty(self.pyobj_garbage_list):
+ self.pyobj_garbage_list.c_gc_prev = gchdr
+ self.pyobj_garbage_list.c_gc_next = gchdr
+ # mark referenced objects alive (so objects in the old list
+ # will be detected as garbage, as they should have a cyclic
+ # refcount of zero or an unmarked linked pypy object)
+ self._traverse(pyobj, 1)
+ if pyobj.c_ob_pypy_link <> 0:
+ #intobj = pyobj.c_ob_pypy_link
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ #obj = llmemory.cast_int_to_adr(intobj)
+ self.garbage_to_trace.append(obj)
+ self.gc.objects_to_trace.append(obj)
+ self.gc.visit_all_objects()
+
+ def _collect_obj(self, obj, ignored):
+ llop.debug_nonnull_pointer(lltype.Void, obj)
+ self.garbage_to_trace.append(obj)
+ _collect_obj._always_inline_ = True
+
+ def _collect_ref_rec(self, root, ignored):
+ self._collect_obj(root.address[0], None)
+
+ def _garbage_visit(self, obj):
+ # If GCFLAG_GARBAGE is set, remove the flag and trace the object
+ hdr = self.gc.header(obj)
+ if not (hdr.tid & self.GCFLAG_GARBAGE):
+ return False
+ hdr.tid &= ~self.GCFLAG_GARBAGE
+ if self.gc.has_gcptr(llop.extract_ushort(llgroup.HALFWORD, hdr.tid)):
+ self.gc.trace(obj, self._collect_ref_rec, None)
+ return True
+
+ def _check_finalizer(self):
+ # Check, if the cyclic isolate from the last collection cycle
+ # is reachable from outside, after the finalizers have been
+ # executed (and if all finalizers have been executed).
+ found_alive = self._gc_list_is_empty(self.pyobj_isolate_list)
+ if not found_alive:
+ found_alive = self._find_finalizer()
+ if not found_alive:
+ self._collect_roots(self.pyobj_old_list)
+ gchdr = self.pyobj_old_list.c_gc_next
+ while gchdr <> self.pyobj_old_list:
+ if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0:
+ found_alive = True
+ break
+ gchdr = gchdr.c_gc_next
+ if found_alive:
+ self._gc_list_merge(self.pyobj_old_list, self.pyobj_list)
+ return False
+ else:
+ self._gc_list_merge(self.pyobj_old_list, self.pyobj_dead_list)
+ return True
+
+ def _find_finalizer(self):
+ gchdr = self.pyobj_old_list.c_gc_next
+ while gchdr <> self.pyobj_old_list:
+ if self.finalizer_type(gchdr) == \
+ self.RAWREFCOUNT_FINALIZER_MODERN:
+ return True
+ gchdr = gchdr.c_gc_next
+ return False
+
+ def _visit(pyobj, self_ptr):
+ from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance
+ #
+ self_adr = rffi.cast(llmemory.Address, self_ptr)
+ self = cast_adr_to_nongc_instance(RawRefCountBaseGC, self_adr)
+ self._visit_action(pyobj, None)
+ return rffi.cast(rffi.INT_real, 0)
+
+ def _visit_action(self, pyobj, ignore):
+ pygchdr = self.pyobj_as_gc(pyobj)
+ if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR):
+ if pygchdr.c_gc_refs != self.RAWREFCOUNT_REFS_UNTRACKED:
+ pygchdr.c_gc_refs += self.refcnt_add << \
+ self.RAWREFCOUNT_REFS_SHIFT
+ elif pyobj.c_ob_pypy_link != 0:
+ pyobj.c_ob_refcnt += self.refcnt_add
+ if self.refcnt_add > 0:
+ #intobj = pyobj.c_ob_pypy_link
+ #obj = llmemory.cast_int_to_adr(intobj)
+ pyobject = llmemory.cast_ptr_to_adr(pyobj)
+ obj = self.refcnt_dict.get(pyobject)
+ self.gc.objects_to_trace.append(obj)
+ self.gc.visit_all_objects()
+
+ def _traverse(self, pyobj, refcnt_add):
+ from rpython.rlib.objectmodel import we_are_translated
+ from rpython.rtyper.annlowlevel import (cast_nongc_instance_to_adr,
+ llhelper)
+ #
+ self.refcnt_add = refcnt_add
+ if we_are_translated():
+ callback_ptr = llhelper(self.RAWREFCOUNT_VISIT,
+ RawRefCountBaseGC._visit)
+ self_ptr = rffi.cast(rffi.VOIDP, cast_nongc_instance_to_adr(self))
+ self.tp_traverse(pyobj, callback_ptr, self_ptr)
+ else:
+ self.tp_traverse(pyobj, self._visit_action, None)
+
+ # --- Helpers ---
+
+ def _gc_list_new(self):
+ list = lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True)
+ self._gc_list_init(list)
+ return list
+
+ def _gc_list_init(self, pygclist):
+ pygclist.c_gc_next = pygclist
+ pygclist.c_gc_prev = pygclist
+
+ def _gc_list_add(self, pygclist, gchdr):
+ next = pygclist.c_gc_next
+ pygclist.c_gc_next = gchdr
+ gchdr.c_gc_prev = pygclist
+ gchdr.c_gc_next = next
+ next.c_gc_prev = gchdr
+
+ def _gc_list_pop(self, pygclist):
+ ret = pygclist.c_gc_next
+ pygclist.c_gc_next = ret.c_gc_next
+ ret.c_gc_next.c_gc_prev = pygclist
+ return ret
+
+ def _gc_list_move(self, pygclist_source, pygclist_dest):
+ pygclist_dest.c_gc_next = pygclist_source.c_gc_next
+ pygclist_dest.c_gc_prev = pygclist_source.c_gc_prev
+ pygclist_dest.c_gc_next.c_gc_prev = pygclist_dest
+ pygclist_dest.c_gc_prev.c_gc_next = pygclist_dest
+ pygclist_source.c_gc_next = pygclist_source
+ pygclist_source.c_gc_prev = pygclist_source
+
+ def _gc_list_merge(self, pygclist_source, pygclist_dest):
+ next = pygclist_dest.c_gc_next
+ next_old = pygclist_source.c_gc_next
+ prev_old = pygclist_source.c_gc_prev
+ pygclist_dest.c_gc_next = next_old
+ next_old.c_gc_prev = pygclist_dest
+ prev_old.c_gc_next = next
+ next.c_gc_prev = prev_old
+ pygclist_source.c_gc_next = pygclist_source
+ pygclist_source.c_gc_prev = pygclist_source
+
+ def _gc_list_is_empty(self, pygclist):
+ return pygclist.c_gc_next == pygclist
+
+ # --- Tests / Debugging ---
+
+ def check_no_more_rawrefcount_state(self):
+ "NOT_RPYTHON: for tests"
+ assert self.p_list_young.length() == 0
+ assert self.p_list_old .length() == 0
+ assert self.o_list_young.length() == 0
+ assert self.o_list_old .length() == 0
+ def check_value_is_null(key, value, ignore):
+ assert value == llmemory.NULL
+ self.p_dict.foreach(check_value_is_null, None)
+ self.p_dict_nurs.foreach(check_value_is_null, None)
+
+ def _debug_check_consistency(self, print_label=None):
+ if self.gc.DEBUG:
+ should_print = print_label is not None
+ if should_print:
+ debug_start("rrc-lists " + print_label)
+ self._debug_check_list(self.pyobj_list, should_print, "pyobj_list")
+ self._debug_check_list(self.tuple_list, should_print, "tuple_list")
+ self._debug_check_list(self.pyobj_old_list, should_print,
+ "pyobj_old_list")
+ self._debug_check_list(self.pyobj_dead_list, should_print,
+ "pyobj_dead_list")
+ self._debug_check_list(self.pyobj_isolate_list, should_print,
+ "pyobj_isolate_list")
+ # pyobj_garbage_list is not a real list, it just marks the
+ # first and the last object in pyobj_list, which are garbage
+
+ if should_print:
+ debug_stop("rrc-lists " + print_label)
+
+ def _debug_check_list(self, list, should_print, print_label):
+ if should_print:
+ debug_start(print_label)
+ gchdr = list.c_gc_next
+ prev = list
+ while gchdr <> list:
+ if should_print:
+ pyobj = self.gc_as_pyobj(gchdr)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit