Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-trialdeletion
Changeset: r92281:2fb71fc31ef4
Date: 2017-08-06 13:48 +0200
http://bitbucket.org/pypy/pypy/changeset/2fb71fc31ef4/
Log: Implemented non-incremental cycle detection, removed simple trial
deletion
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -15,6 +15,10 @@
from rpython.rlib.objectmodel import keepalive_until_here
from rpython.rtyper.annlowlevel import llhelper
from rpython.rlib import rawrefcount
+from rpython.rlib.rawrefcount import (
+ REFCNT_MASK, REFCNT_FROM_PYPY, REFCNT_OVERFLOW, REFCNT_CYCLE_BUFFERED,
+ REFCNT_CLR_MASK, REFCNT_CLR_GREEN, REFCNT_CLR_PURPLE,
+ W_MARKER_DEALLOCATING)
from rpython.rlib.debug import fatalerror, debug_print
from pypy.module.cpyext.api import slot_function
from pypy.module.cpyext.typeobjectdefs import visitproc
@@ -190,9 +194,6 @@
py_obj.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY
rawrefcount.create_link_pypy(w_obj, py_obj)
-
-w_marker_deallocating = W_Root()
-
def from_ref(space, ref):
"""
Finds the interpreter object corresponding to the given reference. If the
@@ -203,7 +204,7 @@
return None
w_obj = rawrefcount.to_obj(W_Root, ref)
if w_obj is not None:
- if w_obj is not w_marker_deallocating:
+ if w_obj is not W_MARKER_DEALLOCATING:
return w_obj
fatalerror(
"*** Invalid usage of a dying CPython object ***\n"
@@ -250,7 +251,7 @@
def pyobj_has_w_obj(pyobj):
w_obj = rawrefcount.to_obj(W_Root, pyobj)
- return w_obj is not None and w_obj is not w_marker_deallocating
+ return w_obj is not None and w_obj is not W_MARKER_DEALLOCATING
def is_pyobj(x):
@@ -270,27 +271,6 @@
hop.exception_cannot_occur()
return hop.inputconst(lltype.Bool, hop.s_result.const)
-def _decref(pyobj):
- if pyobj.c_ob_refcnt & rawrefcount.REFCNT_OVERFLOW == 0:
- pyobj.c_ob_refcnt -= 1
- else:
- if pyobj.c_ob_refcnt & rawrefcount.REFCNT_MASK \
- == rawrefcount.REFCNT_OVERFLOW:
- pyobj.c_ob_refcnt -= 1
- elif rawrefcount.overflow_sub(pyobj):
- pyobj.c_ob_refcnt -= 1
-
-def _incref(pyobj):
- if pyobj.c_ob_refcnt & rawrefcount.REFCNT_OVERFLOW == 0:
- pyobj.c_ob_refcnt += 1
- else:
- if pyobj.c_ob_refcnt & rawrefcount.REFCNT_MASK \
- == rawrefcount.REFCNT_OVERFLOW:
- pyobj.c_ob_refcnt += 1
- rawrefcount.overflow_new(pyobj)
- else:
- rawrefcount.overflow_add(pyobj)
-
@specialize.ll()
def make_ref(space, obj, w_userdata=None):
"""Increment the reference counter of the PyObject and return it.
@@ -301,7 +281,7 @@
else:
pyobj = as_pyobj(space, obj, w_userdata)
if pyobj:
- _incref(pyobj)
+ rawrefcount.incref(pyobj)
if not is_pyobj(obj):
keepalive_until_here(obj)
return pyobj
@@ -321,7 +301,7 @@
w_obj = obj
pyobj = as_pyobj(space, w_obj)
if pyobj:
- _decref(pyobj)
+ rawrefcount.decref(pyobj)
keepalive_until_here(w_obj)
return w_obj
@@ -334,122 +314,30 @@
if is_pyobj(obj):
obj = rffi.cast(PyObject, obj)
if obj:
- _decref(obj)
-
- if obj.c_ob_refcnt & rawrefcount.REFCNT_MASK == 0 and \
- rawrefcount.get_trialdeletion_phase() != 1:
- if obj.c_ob_refcnt & rawrefcount.REFCNT_FROM_PYPY == 0:
+ rawrefcount.decref(obj)
+ rc = obj.c_ob_refcnt
+ if (rc & REFCNT_MASK == 0):
+ if (rc & REFCNT_FROM_PYPY == 0 and
+ rc & REFCNT_CLR_MASK != REFCNT_CLR_PURPLE):
_Py_Dealloc(space, obj)
- elif obj.c_ob_refcnt & rawrefcount.REFCNT_CLR_GREEN == 0:
- if rawrefcount.get_trialdeletion_phase() == 0:
- trial_delete(space, obj)
+ elif (rc & REFCNT_CLR_MASK != REFCNT_CLR_GREEN):
+ possible_root(space, obj)
else:
get_w_obj_and_decref(space, obj)
[email protected]()
-def refcnt_overflow(space, obj):
- if is_pyobj(obj):
- pyobj = rffi.cast(PyObject, obj)
- else:
- pyobj = as_pyobj(space, obj, None)
- if pyobj:
- if (pyobj.c_ob_refcnt & rawrefcount.REFCNT_MASK ==
- rawrefcount.REFCNT_OVERFLOW):
- return rawrefcount.REFCNT_OVERFLOW
- else:
- return (pyobj.c_ob_refcnt & rawrefcount.REFCNT_MASK) \
- + rawrefcount.overflow_get(pyobj)
- return 0
-
-def traverse(space, obj, visit):
- from pypy.module.cpyext.api import generic_cpy_call
- if obj.c_ob_type and obj.c_ob_type.c_tp_traverse:
- generic_cpy_call(space, obj.c_ob_type.c_tp_traverse, obj, visit,
- rffi.cast(rffi.VOIDP, obj))
-
-def clear(space, obj):
- from pypy.module.cpyext.api import generic_cpy_call
- if obj.c_ob_type:
- generic_cpy_call(space, obj.c_ob_type.c_tp_clear, obj)
-
-@slot_function([PyObject, rffi.VOIDP], rffi.INT_real, error=-1)
-def visit_decref(space, obj, args):
- _decref(obj)
- debug_print("visited dec", obj, "new refcnt", obj.c_ob_refcnt)
- if (obj not in rawrefcount.get_visited()):
- rawrefcount.add_visited(obj)
- from pypy.module.cpyext.slotdefs import llslot
- traverse(space, obj, rffi.cast(visitproc, llslot(space, visit_decref)))
- return 0
-
-@slot_function([PyObject, rffi.VOIDP], rffi.INT_real, error=-1)
-def visit_incref(space, obj, args):
- _incref(obj)
- debug_print("visited inc", obj, "new refcnt", obj.c_ob_refcnt)
- if (obj not in rawrefcount.get_visited()):
- rawrefcount.add_visited(obj)
- from pypy.module.cpyext.slotdefs import llslot
- traverse(space, obj, rffi.cast(visitproc, llslot(space, visit_incref)))
- return 0
-
[email protected]()
-def trial_delete(space, obj):
+def possible_root(space, obj):
+ debug_print("possible root", obj)
+ rc = obj.c_ob_refcnt
if not obj.c_ob_type or not obj.c_ob_type.c_tp_traverse:
- obj.c_ob_refcnt = obj.c_ob_refcnt | rawrefcount.REFCNT_CLR_GREEN
- return
-
- from pypy.module.cpyext.slotdefs import llslot
- visitproc_incref = rffi.cast(visitproc, llslot(space, visit_incref))
- visitproc_decref = rffi.cast(visitproc, llslot(space, visit_decref))
-
- rawrefcount.set_trialdeletion_phase(1)
-
- debug_print("trial_delete", obj, "refct after decref", obj.c_ob_refcnt)
-
- debug_print("decref phase")
- rawrefcount.clear_visited()
- rawrefcount.add_visited(obj)
- traverse(space, obj, visitproc_decref)
-
- debug_print("checkref phase")
- visited = []
- alive = []
- for visited_obj in rawrefcount.get_visited():
- visited.append(visited_obj)
- if visited_obj.c_ob_refcnt != 0 and \
- visited_obj.c_ob_refcnt != rawrefcount.REFCNT_FROM_PYPY:
- alive.append(visited_obj)
- debug_print("alive", visited_obj)
-
- debug_print("incref phase")
- rawrefcount.clear_visited()
- for alive_obj in alive:
- if alive_obj not in rawrefcount.get_visited():
- rawrefcount.add_visited(alive_obj)
- traverse(space, alive_obj, visitproc_incref)
-
- alive = []
- for alive_obj in rawrefcount.get_visited():
- debug_print("alive", alive_obj, alive_obj.c_ob_refcnt)
- alive.append(alive_obj)
-
- for reachable_obj in visited:
- if reachable_obj not in rawrefcount.get_visited():
- rawrefcount.add_visited(reachable_obj)
- traverse(space, reachable_obj, visitproc_incref)
-
- debug_print("clear phase")
- rawrefcount.set_trialdeletion_phase(2)
-
- for reachable_obj in visited:
- if reachable_obj not in alive:
- if reachable_obj.c_ob_refcnt < rawrefcount.REFCNT_FROM_PYPY \
- and reachable_obj.c_ob_refcnt > 0:
- debug_print("clear", reachable_obj)
- clear(space, reachable_obj)
-
- rawrefcount.set_trialdeletion_phase(0)
- rawrefcount.clear_visited()
+ debug_print("mark green", obj)
+ rc = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_GREEN
+ elif rc & REFCNT_CLR_MASK != REFCNT_CLR_PURPLE:
+ rc = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_PURPLE
+ if rc & REFCNT_CYCLE_BUFFERED == 0:
+ debug_print("mark purple", obj)
+ rawrefcount.buffer_pyobj(obj)
+ rc = rc | REFCNT_CYCLE_BUFFERED
+ obj.c_ob_refcnt = rc
@cpython_api([PyObject], lltype.Void)
def Py_IncRef(space, obj):
@@ -463,6 +351,20 @@
def _Py_RefCnt_Overflow(space, obj):
return refcnt_overflow(space, obj)
[email protected]()
+def refcnt_overflow(space, obj):
+ if is_pyobj(obj):
+ pyobj = rffi.cast(PyObject, obj)
+ else:
+ pyobj = as_pyobj(space, obj, None)
+ if pyobj:
+ if (pyobj.c_ob_refcnt & REFCNT_MASK == REFCNT_OVERFLOW):
+ return REFCNT_OVERFLOW
+ else:
+ return (pyobj.c_ob_refcnt & REFCNT_MASK) + \
+ rawrefcount.overflow_get(pyobj)
+ return 0
+
@cpython_api([PyObject], lltype.Void)
def _Py_NewReference(space, obj):
obj.c_ob_refcnt = 1
@@ -477,7 +379,7 @@
pto = obj.c_ob_type
#print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \
# "'s type which is", rffi.charp2str(pto.c_tp_name)
- rawrefcount.mark_deallocating(w_marker_deallocating, obj)
+ rawrefcount.mark_deallocating(W_MARKER_DEALLOCATING, obj)
generic_cpy_call(space, pto.c_tp_dealloc, obj)
@cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
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
@@ -73,6 +73,8 @@
from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop
from rpython.rlib.objectmodel import specialize
from rpython.memory.gc.minimarkpage import out_of_memory
+from pypy.module.cpyext.api import slot_function, PyObject
+from rpython.rtyper.lltypesystem import rffi
#
# Handles the objects in 2 generations:
@@ -187,6 +189,105 @@
('forw', llmemory.Address))
FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB)
NURSARRAY = lltype.Array(llmemory.Address)
+VISIT_FUNCTYPE = rffi.CCallback([PyObject, rffi.VOIDP],
+ rffi.INT_real)
+
+def traverse(obj, func_ptr):
+ from pypy.module.cpyext.api import generic_cpy_call
+ from pypy.module.cpyext.typeobjectdefs import visitproc
+ if obj.c_ob_type and obj.c_ob_type.c_tp_traverse:
+ visitproc_ptr = rffi.cast(visitproc, func_ptr)
+ generic_cpy_call(True, obj.c_ob_type.c_tp_traverse, obj,
+ visitproc_ptr, rffi.cast(rffi.VOIDP, obj))
+
+def visit_mark_gray(obj, args):
+ from rpython.rlib.rawrefcount import (REFCNT_CLR_GREEN,
+ REFCNT_CLR_MASK,
+ decref)
+ decref(obj)
+ rc = obj.c_ob_refcnt
+ if rc & REFCNT_CLR_MASK != REFCNT_CLR_GREEN:
+ mark_gray_recursive(obj)
+ return rffi.cast(rffi.INT_real, 0)
+
+def mark_gray_recursive(obj):
+ from rpython.rlib.rawrefcount import (REFCNT_CLR_GRAY,
+ REFCNT_CLR_MASK)
+ from rpython.rtyper.annlowlevel import llhelper
+ debug_print("mark_gray_recursive", obj)
+ rc = obj.c_ob_refcnt
+ if rc & REFCNT_CLR_MASK != REFCNT_CLR_GRAY:
+ obj.c_ob_refcnt = obj.c_ob_refcnt & ~REFCNT_CLR_MASK | REFCNT_CLR_GRAY
+ func_ptr = llhelper(VISIT_FUNCTYPE, visit_mark_gray)
+ traverse(obj, func_ptr)
+
+def visit_scan_black(obj, args):
+ from rpython.rlib.rawrefcount import (REFCNT_CLR_BLACK,
+ REFCNT_CLR_MASK,
+ REFCNT_CLR_GREEN,
+ incref)
+ incref(obj)
+ rc = obj.c_ob_refcnt
+ if (rc & REFCNT_CLR_MASK != REFCNT_CLR_BLACK and
+ rc & REFCNT_CLR_MASK != REFCNT_CLR_GREEN):
+ scan_black_recursive(obj)
+ return rffi.cast(rffi.INT_real, 0)
+
+def scan_black_recursive(obj):
+ from rpython.rlib.rawrefcount import (REFCNT_CLR_BLACK,
+ REFCNT_CLR_MASK)
+ from rpython.rtyper.annlowlevel import llhelper
+ debug_print("scan_black_recursive", obj)
+ rc = obj.c_ob_refcnt
+ obj.c_ob_refcnt = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_BLACK
+ func_ptr = llhelper(VISIT_FUNCTYPE, visit_scan_black)
+ traverse(obj, func_ptr)
+
+def visit_scan(obj, args):
+ scan_recursive(obj)
+ return rffi.cast(rffi.INT_real, 0)
+
+def scan_recursive(obj):
+ from rpython.rlib.rawrefcount import (REFCNT_CLR_WHITE,
+ REFCNT_CLR_GRAY,
+ REFCNT_CLR_GREEN,
+ REFCNT_CLR_MASK,
+ REFCNT_MASK)
+ from rpython.rtyper.annlowlevel import llhelper
+ debug_print("scan_recursive", obj)
+ rc = obj.c_ob_refcnt
+ if (rc & REFCNT_CLR_MASK == REFCNT_CLR_GRAY or
+ rc & REFCNT_CLR_MASK == REFCNT_CLR_GREEN):
+ if rc & REFCNT_MASK > 0 and rc & REFCNT_CLR_MASK != REFCNT_CLR_GREEN:
+ scan_black_recursive(obj)
+ else:
+ obj.c_ob_refcnt = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_WHITE
+ func_ptr = llhelper(VISIT_FUNCTYPE, visit_scan)
+ traverse(obj, func_ptr)
+
+def visit_collect_white(obj, args):
+ collect_white_recursive(obj)
+ return rffi.cast(rffi.INT_real, 0)
+
+def collect_white_recursive(obj):
+ from rpython.rlib.rawrefcount import (REFCNT_CLR_WHITE,
+ REFCNT_CLR_BLACK,
+ REFCNT_CLR_MASK,
+ REFCNT_CYCLE_BUFFERED,
+ REFCNT_FROM_PYPY)
+ from pypy.module.cpyext.api import generic_cpy_call
+ from rpython.rtyper.annlowlevel import llhelper
+ debug_print("collect_white_recursive", obj)
+ rc = obj.c_ob_refcnt
+ if (rc & REFCNT_CLR_MASK == REFCNT_CLR_WHITE and
+ rc & REFCNT_CYCLE_BUFFERED == 0):
+ obj.c_ob_refcnt = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_BLACK
+ func_ptr = llhelper(VISIT_FUNCTYPE, visit_collect_white)
+ traverse(obj, func_ptr)
+ if (rc & REFCNT_FROM_PYPY == 0 and
+ obj.c_ob_type and obj.c_ob_type.c_tp_free):
+ debug_print("free", obj)
+ generic_cpy_call(True, obj.c_ob_type.c_tp_free, obj)
# ____________________________________________________________
@@ -1685,6 +1786,7 @@
#
# visit the P list from rawrefcount, if enabled.
if self.rrc_enabled:
+ self.rrc_collect_cycles() # TODO only for testing
self.rrc_minor_collection_trace()
#
# visit the "probably young" objects with finalizers. They
@@ -2303,6 +2405,7 @@
self.visit_all_objects()
#
if self.rrc_enabled:
+ self.rrc_collect_cycles()
self.rrc_major_collection_trace()
#
ll_assert(not (self.probably_young_objects_with_finalizers
@@ -2901,13 +3004,13 @@
_ADDRARRAY = lltype.Array(llmemory.Address, hints={'nolength': True})
PYOBJ_HDR = lltype.Struct('GCHdr_PyObject',
- ('ob_refcnt', lltype.Signed),
- ('ob_pypy_link', lltype.Signed))
+ ('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))
def _pyobj(self, pyobjaddr):
- return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR)
+ return llmemory.cast_adr_to_ptr(pyobjaddr, lltype.Ptr(PyObject.TO))
def rawrefcount_init(self, dealloc_trigger_callback):
# see pypy/doc/discussion/rawrefcount.rst
@@ -2916,6 +3019,7 @@
self.rrc_p_list_old = self.AddressStack()
self.rrc_o_list_young = self.AddressStack()
self.rrc_o_list_old = self.AddressStack()
+ self.rrc_buffered = 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
@@ -2937,7 +3041,7 @@
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).ob_pypy_link = objint
+ self._pyobj(pyobject).c_ob_pypy_link = objint
#
lst = self.rrc_p_list_young
if self.is_in_nursery(obj):
@@ -2957,14 +3061,17 @@
else:
self.rrc_o_list_old.append(pyobject)
objint = llmemory.cast_adr_to_int(obj, "symbolic")
- self._pyobj(pyobject).ob_pypy_link = objint
+ self._pyobj(pyobject).c_ob_pypy_link = objint
# there is no rrc_o_dict
+ def rawrefcount_buffer_pyobj(self, pyobject):
+ self.rrc_buffered.append(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).ob_pypy_link = objint
+ self._pyobj(pyobject).c_ob_pypy_link = objint
def rawrefcount_from_obj(self, gcobj):
obj = llmemory.cast_ptr_to_adr(gcobj)
@@ -2975,7 +3082,7 @@
return dct.get(obj)
def rawrefcount_to_obj(self, pyobject):
- obj = llmemory.cast_int_to_adr(self._pyobj(pyobject).ob_pypy_link)
+ obj = llmemory.cast_int_to_adr(self._pyobj(pyobject).c_ob_pypy_link)
return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
def rawrefcount_next_dead(self):
@@ -2996,15 +3103,13 @@
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
+ from rpython.rlib.rawrefcount import REFCNT_MASK
#
- rc = self._pyobj(pyobject).ob_refcnt
- if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
+ rc = self._pyobj(pyobject).c_ob_refcnt
+ if rc & REFCNT_MASK == 0:
pass # the corresponding object may die
else:
- # force the corresponding object to be alive
- intobj = self._pyobj(pyobject).ob_pypy_link
+ intobj = self._pyobj(pyobject).c_ob_pypy_link
singleaddr.address[0] = llmemory.cast_int_to_adr(intobj)
self._trace_drag_out1(singleaddr)
@@ -3021,14 +3126,14 @@
no_o_dict)
def _rrc_minor_free(self, pyobject, surviving_list, surviving_dict):
- intobj = self._pyobj(pyobject).ob_pypy_link
+ 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).ob_pypy_link = intobj
+ self._pyobj(pyobject).c_ob_pypy_link = intobj
surviving = True
if surviving_dict:
# Surviving nursery object: was originally in
@@ -3059,23 +3164,24 @@
def _rrc_free(self, pyobject):
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ from rpython.rlib.rawrefcount import REFCNT_MASK
#
- rc = self._pyobj(pyobject).ob_refcnt
+ rc = self._pyobj(pyobject).c_ob_refcnt
if rc >= REFCNT_FROM_PYPY_LIGHT:
rc -= REFCNT_FROM_PYPY_LIGHT
- if rc == 0:
+ if rc & REFCNT_MASK == 0:
lltype.free(self._pyobj(pyobject), flavor='raw')
else:
# can only occur if LIGHT is used in create_link_pyobj()
- self._pyobj(pyobject).ob_refcnt = rc
- self._pyobj(pyobject).ob_pypy_link = 0
+ 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).ob_pypy_link = 0
- if rc == 0:
+ self._pyobj(pyobject).c_ob_pypy_link = 0
+ if rc & REFCNT_MASK == 0:
self.rrc_dealloc_pending.append(pyobject)
# an object with refcnt == 0 cannot stay around waiting
# for its deallocator to be called. Some code (lxml)
@@ -3086,22 +3192,62 @@
# because after a Py_INCREF()/Py_DECREF() on it, its
# tp_dealloc is also called!
rc = 1
- self._pyobj(pyobject).ob_refcnt = rc
+ self._pyobj(pyobject).c_ob_refcnt = rc
_rrc_free._always_inline_ = True
+ def rrc_collect_cycles(self):
+ self.rrc_buffered.foreach(self._rrc_cycle_mark_roots, None)
+ self.rrc_buffered.foreach(self._rrc_cycle_scan_roots, None)
+ self.rrc_buffered.foreach(self._rrc_cycle_collect_roots, None)
+
+ def _rrc_cycle_mark_roots(self, pyobject, ignore):
+ from pypy.module.cpyext.api import generic_cpy_call
+ from rpython.rlib.rawrefcount import (REFCNT_CYCLE_BUFFERED,
+ REFCNT_CLR_MASK,
+ REFCNT_CLR_PURPLE,
+ REFCNT_MASK,
+ W_MARKER_DEALLOCATING,
+ mark_deallocating)
+ obj = self._pyobj(pyobject)
+ rc = obj.c_ob_refcnt
+ debug_print("_rrc_cycle_mark_roots", obj)
+ if rc & REFCNT_CLR_MASK == REFCNT_CLR_PURPLE and \
+ rc & REFCNT_MASK > 0:
+ mark_gray_recursive(obj)
+ else:
+ obj.c_ob_refcnt = rc & ~REFCNT_CYCLE_BUFFERED
+ self.rrc_buffered.remove(pyobject)
+ if rc & REFCNT_MASK == 0:
+ mark_deallocating(W_MARKER_DEALLOCATING, obj)
+ generic_cpy_call(True, obj.c_ob_type.c_tp_dealloc, obj)
+
+ def _rrc_cycle_scan_roots(self, pyobject, ignore):
+ obj = self._pyobj(pyobject)
+ debug_print("_rrc_cycle_scan_roots", obj)
+ scan_recursive(obj)
+
+ def _rrc_cycle_collect_roots(self, pyobject, ignore):
+ from rpython.rlib.rawrefcount import REFCNT_CYCLE_BUFFERED
+ obj = self._pyobj(pyobject)
+ debug_print("_rrc_cycle_collect_roots", obj)
+ self.rrc_buffered.remove(pyobject)
+ obj.c_ob_refcnt = obj.c_ob_refcnt & ~REFCNT_CYCLE_BUFFERED
+ collect_white_recursive(obj)
+
def rrc_major_collection_trace(self):
self.rrc_p_list_old.foreach(self._rrc_major_trace, None)
def _rrc_major_trace(self, pyobject, ignore):
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
- from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+ from rpython.rlib.rawrefcount import (REFCNT_FROM_PYPY,
+ REFCNT_FROM_PYPY_LIGHT,
+ REFCNT_MASK)
#
- rc = self._pyobj(pyobject).ob_refcnt
- if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT:
+ rc = self._pyobj(pyobject).c_ob_refcnt
+ if rc & REFCNT_MASK == 0:
pass # the corresponding object may die
else:
# force the corresponding object to be alive
- intobj = self._pyobj(pyobject).ob_pypy_link
+ intobj = self._pyobj(pyobject).c_ob_pypy_link
obj = llmemory.cast_int_to_adr(intobj)
self.objects_to_trace.append(obj)
self.visit_all_objects()
@@ -3131,7 +3277,7 @@
# 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).ob_pypy_link
+ 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)
diff --git a/rpython/memory/gc/test/test_rawrefcount.py
b/rpython/memory/gc/test/test_rawrefcount.py
--- a/rpython/memory/gc/test/test_rawrefcount.py
+++ b/rpython/memory/gc/test/test_rawrefcount.py
@@ -4,6 +4,7 @@
from rpython.memory.gc.test.test_direct import BaseDirectGCTest
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT
+from pypy.module.cpyext.api import PyObject, PyTypeObject
PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR
PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR
@@ -56,21 +57,22 @@
self._collect(major=False)
p1 = self.stackroots.pop()
p1ref = lltype.cast_opaque_ptr(llmemory.GCREF, p1)
- r1 = lltype.malloc(PYOBJ_HDR, flavor='raw', immortal=create_immortal)
- r1.ob_refcnt = rc
- r1.ob_pypy_link = 0
+ r1 = lltype.malloc(PyObject.TO, flavor='raw', immortal=create_immortal)
+ r1.c_ob_refcnt = rc
+ r1.c_ob_pypy_link = 0
+ r1.c_ob_type = lltype.nullptr(PyTypeObject)
r1addr = llmemory.cast_ptr_to_adr(r1)
if is_pyobj:
assert not is_light
self.gc.rawrefcount_create_link_pyobj(p1ref, r1addr)
else:
self.gc.rawrefcount_create_link_pypy(p1ref, r1addr)
- assert r1.ob_refcnt == rc
- assert r1.ob_pypy_link != 0
+ assert r1.c_ob_refcnt == rc
+ assert r1.c_ob_pypy_link != 0
def check_alive(extra_refcount):
- assert r1.ob_refcnt == rc + extra_refcount
- assert r1.ob_pypy_link != 0
+ assert r1.c_ob_refcnt == rc + extra_refcount
+ assert r1.c_ob_pypy_link != 0
p1ref = self.gc.rawrefcount_to_obj(r1addr)
p1 = lltype.cast_opaque_ptr(lltype.Ptr(S), p1ref)
assert p1.x == intval
@@ -87,13 +89,13 @@
p2 = self.malloc(S)
p2.x = 84
p2ref = lltype.cast_opaque_ptr(llmemory.GCREF, p2)
- r2 = lltype.malloc(PYOBJ_HDR, flavor='raw')
- r2.ob_refcnt = 1
- r2.ob_pypy_link = 0
+ r2 = lltype.malloc(PyObject.TO, flavor='raw')
+ r2.c_ob_refcnt = 1
+ r2.c_ob_pypy_link = 0
r2addr = llmemory.cast_ptr_to_adr(r2)
# p2 and r2 are not linked
- assert r1.ob_pypy_link != 0
- assert r2.ob_pypy_link == 0
+ assert r1.c_ob_pypy_link != 0
+ assert r2.c_ob_pypy_link == 0
assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
assert self.gc.rawrefcount_from_obj(p2ref) == llmemory.NULL
assert self.gc.rawrefcount_to_obj(r1addr) == p1ref
@@ -106,16 +108,16 @@
p1, p1ref, r1, r1addr, check_alive = (
self._rawrefcount_pair(42, is_light=True, create_old=old))
check_alive(0)
- r1.ob_refcnt += 1
+ r1.c_ob_refcnt += 1
self._collect(major=False)
check_alive(+1)
self._collect(major=True)
check_alive(+1)
- r1.ob_refcnt -= 1
+ r1.c_ob_refcnt -= 1
self._collect(major=False)
p1 = check_alive(0)
self._collect(major=True)
- py.test.raises(RuntimeError, "r1.ob_refcnt") # dead
+ py.test.raises(RuntimeError, "r1.c_ob_refcnt") # dead
py.test.raises(RuntimeError, "p1.x") # dead
self.gc.check_no_more_rawrefcount_state()
assert self.trigger == []
@@ -129,7 +131,7 @@
if old:
check_alive(0)
self._collect(major=True)
- py.test.raises(RuntimeError, "r1.ob_refcnt") # dead
+ py.test.raises(RuntimeError, "r1.c_ob_refcnt") # dead
py.test.raises(RuntimeError, "p1.x") # dead
self.gc.check_no_more_rawrefcount_state()
@@ -147,7 +149,7 @@
check_alive(0)
assert p1.x == 42
self._collect(major=True)
- py.test.raises(RuntimeError, "r1.ob_refcnt") # dead
+ py.test.raises(RuntimeError, "r1.c_ob_refcnt") # dead
py.test.raises(RuntimeError, "p1.x") # dead
self.gc.check_no_more_rawrefcount_state()
@@ -164,18 +166,18 @@
p1, p1ref, r1, r1addr, check_alive = (
self._rawrefcount_pair(42, is_light=False, create_old=old))
check_alive(0)
- r1.ob_refcnt += 1
+ r1.c_ob_refcnt += 1
self._collect(major=False)
check_alive(+1)
self._collect(major=True)
check_alive(+1)
- r1.ob_refcnt -= 1
+ r1.c_ob_refcnt -= 1
self._collect(major=False)
p1 = check_alive(0)
self._collect(major=True, expected_trigger=1)
py.test.raises(RuntimeError, "p1.x") # dead
- assert r1.ob_refcnt == 1 # in the pending list
- assert r1.ob_pypy_link == 0
+ assert r1.c_ob_refcnt == 1 # in the pending list
+ assert r1.c_ob_pypy_link == 0
assert self.gc.rawrefcount_next_dead() == r1addr
assert self.gc.rawrefcount_next_dead() == llmemory.NULL
assert self.gc.rawrefcount_next_dead() == llmemory.NULL
@@ -197,8 +199,8 @@
assert p1.x == 42
self._collect(major=True, expected_trigger=1)
py.test.raises(RuntimeError, "p1.x") # dead
- assert r1.ob_refcnt == 1
- assert r1.ob_pypy_link == 0
+ assert r1.c_ob_refcnt == 1
+ assert r1.c_ob_pypy_link == 0
assert self.gc.rawrefcount_next_dead() == r1addr
self.gc.check_no_more_rawrefcount_state()
lltype.free(r1, flavor='raw')
@@ -214,8 +216,8 @@
else:
self._collect(major=False, expected_trigger=1)
py.test.raises(RuntimeError, "p1.x") # dead
- assert r1.ob_refcnt == 1
- assert r1.ob_pypy_link == 0
+ assert r1.c_ob_refcnt == 1
+ assert r1.c_ob_pypy_link == 0
assert self.gc.rawrefcount_next_dead() == r1addr
self.gc.check_no_more_rawrefcount_state()
lltype.free(r1, flavor='raw')
@@ -232,10 +234,10 @@
p1, p1ref, r1, r1addr, check_alive = (
self._rawrefcount_pair(42, is_pyobj=True, force_external=external))
check_alive(0)
- r1.ob_refcnt += 1 # the pyobject is kept alive
+ r1.c_ob_refcnt += 1 # the pyobject is kept alive
self._collect(major=False)
- assert r1.ob_refcnt == 1 # refcnt dropped to 1
- assert r1.ob_pypy_link == 0 # detached
+ assert r1.c_ob_refcnt == 1 # refcnt dropped to 1
+ assert r1.c_ob_pypy_link == 0 # detached
self.gc.check_no_more_rawrefcount_state()
lltype.free(r1, flavor='raw')
@@ -252,8 +254,8 @@
self._collect(major=True, expected_trigger=1)
else:
self._collect(major=False, expected_trigger=1)
- assert r1.ob_refcnt == 1 # refcnt 1, in the pending list
- assert r1.ob_pypy_link == 0 # detached
+ assert r1.c_ob_refcnt == 1 # refcnt 1, in the pending list
+ assert r1.c_ob_pypy_link == 0 # detached
assert self.gc.rawrefcount_next_dead() == r1addr
self.gc.check_no_more_rawrefcount_state()
lltype.free(r1, flavor='raw')
@@ -277,8 +279,8 @@
assert self.trigger == []
self._collect(major=True, expected_trigger=1)
py.test.raises(RuntimeError, "p1.x") # dead
- assert r1.ob_refcnt == 1
- assert r1.ob_pypy_link == 0
+ assert r1.c_ob_refcnt == 1
+ assert r1.c_ob_pypy_link == 0
assert self.gc.rawrefcount_next_dead() == r1addr
self.gc.check_no_more_rawrefcount_state()
lltype.free(r1, flavor='raw')
@@ -289,3 +291,10 @@
check_alive(0)
self._collect(major=True)
check_alive(0)
+
+ def test_cycle(self):
+ p1, p1ref, r1, r1addr, check_alive = (
+ self._rawrefcount_pair(42, is_pyobj=True))
+ self.gc.rawrefcount_buffer_pyobj(r1addr)
+ self.gc.rrc_collect_cycles()
+ lltype.free(r1, flavor='raw')
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
@@ -482,6 +482,10 @@
GCClass.rawrefcount_mark_deallocating,
[s_gc, s_gcref, SomeAddress()],
annmodel.s_None)
+ self.rawrefcount_buffer_pyobj = getfn(
+ GCClass.rawrefcount_buffer_pyobj,
+ [s_gc, SomeAddress()],
+ annmodel.s_None)
self.rawrefcount_from_obj_ptr = getfn(
GCClass.rawrefcount_from_obj, [s_gc, s_gcref], SomeAddress(),
inline = True)
@@ -1292,6 +1296,13 @@
[self.rawrefcount_mark_deallocating, self.c_const_gc,
v_gcobj, v_pyobject])
+ def gct_gc_rawrefcount_buffer_pyobj(self, hop):
+ [v_pyobject] = hop.spaceop.args
+ assert v_pyobject.concretetype == llmemory.Address
+ hop.genop("direct_call",
+ [self.rawrefcount_buffer_pyobj, self.c_const_gc,
+ v_pyobject])
+
def gct_gc_rawrefcount_from_obj(self, hop):
[v_gcobj] = hop.spaceop.args
assert v_gcobj.concretetype == llmemory.GCREF
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -9,7 +9,8 @@
from rpython.rlib.objectmodel import we_are_translated, specialize, not_rpython
from rpython.rtyper.extregistry import ExtRegistryEntry
from rpython.translator.tool.cbuild import ExternalCompilationInfo
-from rpython.rlib import rgc
+from rpython.rlib import rgc, objectmodel
+from pypy.interpreter.baseobjspace import W_Root
MAX_BIT = int(math.log(sys.maxint, 2))
@@ -33,6 +34,7 @@
REFCNT_CLR_GREEN = 4 << REFCNT_CLR_OFFS # Acyclic
REFCNT_CLR_RED = 5 << REFCNT_CLR_OFFS # Cand cycle undergoing SIGMA-comp.
REFCNT_CLR_ORANGE = 6 << REFCNT_CLR_OFFS # Cand cycle awaiting epoch boundary
+REFCNT_CLR_MASK = 7 << REFCNT_CLR_OFFS
# Cyclic reference count with overflow bit
REFCNT_CRC_OVERFLOW = 1 << REFCNT_CRC_OFFS + REFCNT_BITS
@@ -46,6 +48,8 @@
RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
+W_MARKER_DEALLOCATING = W_Root()
+
def _build_pypy_link(p):
res = len(_adr2pypy)
@@ -70,21 +74,41 @@
_refcount_overflow = dict()
+def incref(pyobj):
+ if pyobj.c_ob_refcnt & REFCNT_OVERFLOW == 0:
+ pyobj.c_ob_refcnt += 1
+ else:
+ if pyobj.c_ob_refcnt & REFCNT_MASK == REFCNT_OVERFLOW:
+ pyobj.c_ob_refcnt += 1
+ overflow_new(pyobj)
+ else:
+ overflow_add(pyobj)
+
+def decref(pyobj):
+ if pyobj.c_ob_refcnt & REFCNT_OVERFLOW == 0:
+ pyobj.c_ob_refcnt -= 1
+ else:
+ if pyobj.c_ob_refcnt & REFCNT_MASK == REFCNT_OVERFLOW:
+ pyobj.c_ob_refcnt -= 1
+ elif overflow_sub(pyobj):
+ pyobj.c_ob_refcnt -= 1
+
# TODO: if object moves, address changes!
def overflow_new(obj):
- _refcount_overflow[id(obj)] = 0
+ _refcount_overflow[objectmodel.current_object_addr_as_int(obj)] = 0
def overflow_add(obj):
- _refcount_overflow[id(obj)] += 1
+ _refcount_overflow[objectmodel.current_object_addr_as_int(obj)] += 1
def overflow_sub(obj):
- c = _refcount_overflow[id(obj)]
+ addr = objectmodel.current_object_addr_as_int(obj)
+ c = _refcount_overflow[addr]
if c > 0:
- _refcount_overflow[id(obj)] = c - 1
+ _refcount_overflow[addr] = c - 1
return False
else:
- _refcount_overflow.pop(id(obj))
+ _refcount_overflow.pop(addr)
return True
def overflow_get(obj):
- return _refcount_overflow[id(obj)]
+ return _refcount_overflow[objectmodel.current_object_addr_as_int(obj)]
# TODO: _cyclic_refcount_overflow = dict()
@@ -136,6 +160,10 @@
ob.c_ob_pypy_link = _build_pypy_link(marker)
@not_rpython
+def buffer_pyobj(ob):
+ pass # TODO: implement?
+
+@not_rpython
def from_obj(OB_PTR_TYPE, p):
ob = _pypy2ob.get(p)
if ob is None:
@@ -321,6 +349,19 @@
class Entry(ExtRegistryEntry):
+ _about_ = buffer_pyobj
+
+ def compute_result_annotation(self, s_ob):
+ pass
+
+ def specialize_call(self, hop):
+ name = 'gc_rawrefcount_buffer_pyobj'
+ hop.exception_cannot_occur()
+ v_ob = hop.inputarg(hop.args_r[0], arg=0)
+ hop.genop(name, [_unspec_ob(hop, v_ob)])
+
+
+class Entry(ExtRegistryEntry):
_about_ = from_obj
def compute_result_annotation(self, s_OB_PTR_TYPE, s_p):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -522,8 +522,8 @@
translator = hop.rtyper.annotator.translator
fq = hop.args_s[0].const
graph = translator._graphof(fq.finalizer_trigger.im_func)
- #InstanceRepr.check_graph_of_del_does_not_call_too_much(hop.rtyper,
- # graph)
+ InstanceRepr.check_graph_of_del_does_not_call_too_much(hop.rtyper,
+ graph)
hop.exception_cannot_occur()
return hop.inputconst(lltype.Signed, hop.s_result.const)
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -966,6 +966,9 @@
def op_gc_rawrefcount_mark_deallocating(self, *args):
raise NotImplementedError("gc_rawrefcount_mark_deallocating")
+ def op_gc_rawrefcount_buffer_pyobj(self, *args):
+ raise NotImplementedError("gc_rawrefcount_buffer_pyobj")
+
def op_gc_rawrefcount_next_dead(self, *args):
raise NotImplementedError("gc_rawrefcount_next_dead")
diff --git a/rpython/rtyper/lltypesystem/lloperation.py
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -492,6 +492,7 @@
'gc_rawrefcount_create_link_pypy': LLOp(),
'gc_rawrefcount_create_link_pyobj': LLOp(),
'gc_rawrefcount_mark_deallocating': LLOp(),
+ 'gc_rawrefcount_buffer_pyobj': LLOp(),
'gc_rawrefcount_from_obj': LLOp(sideeffects=False),
'gc_rawrefcount_to_obj': LLOp(sideeffects=False),
'gc_rawrefcount_next_dead': LLOp(),
diff --git a/rpython/rtyper/lltypesystem/lltype.py
b/rpython/rtyper/lltypesystem/lltype.py
--- a/rpython/rtyper/lltypesystem/lltype.py
+++ b/rpython/rtyper/lltypesystem/lltype.py
@@ -564,7 +564,7 @@
def _container_example(self):
def ex(*args):
return self.RESULT._defl()
- return _func(self, _callable=ex)
+ return _func(self, {'_callable': ex})
def _trueargs(self):
return [arg for arg in self.ARGS if arg is not Void]
@@ -2094,7 +2094,7 @@
class _func(_container):
- def __init__(self, TYPE, **attrs):
+ def __init__(self, TYPE, attrs):
attrs.setdefault('_TYPE', TYPE)
attrs.setdefault('_name', '?')
attrs.setdefault('_callable', None)
@@ -2303,7 +2303,8 @@
hash(tuple(attrs.items()))
except TypeError:
raise TypeError("'%r' must be hashable"%attrs)
- o = _func(TYPE, _name=name, **attrs)
+ attrs['_name'] = name
+ o = _func(TYPE, attrs)
return _ptr(Ptr(TYPE), o)
def _getconcretetype(v):
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -585,8 +585,8 @@
assert len(s_func.descriptions) == 1
funcdesc, = s_func.descriptions
graph = funcdesc.getuniquegraph()
- #self.check_graph_of_del_does_not_call_too_much(self.rtyper,
- # graph)
+ self.check_graph_of_del_does_not_call_too_much(self.rtyper,
+ graph)
FUNCTYPE = FuncType([Ptr(source_repr.object_type)], Void)
destrptr = functionptr(FUNCTYPE, graph.name,
graph=graph,
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit