Author: Stefan Beyer <[email protected]>
Branch: cpyext-gc-cycle
Changeset: r96723:0b73323d80cc
Date: 2019-05-30 15:11 +0200
http://bitbucket.org/pypy/pypy/changeset/0b73323d80cc/
Log: Fixed some issues with tracking/deallocating cpyext gc objects Added
rudimentary support for lists (gc issues, clear missing)
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -1169,6 +1169,11 @@
[rffi.VOIDP], lltype.Void,
compilation_info=eci,
_nowrapper=True)
+ state.C.PyObject_GC_Del = rffi.llexternal(
+ mangle_name(prefix, 'PyObject_GC_Del'),
+ [rffi.VOIDP], lltype.Void,
+ compilation_info=eci,
+ _nowrapper=True)
state.C.PyType_GenericAlloc = rffi.llexternal(
mangle_name(prefix, 'PyType_GenericAlloc'),
[PyTypeObjectPtr, Py_ssize_t], PyObject,
@@ -1186,6 +1191,9 @@
'_PyPy_tuple_free', [rffi.VOIDP], lltype.Void,
compilation_info=eci, _nowrapper=True)
from pypy.module.cpyext.typeobjectdefs import visitproc
+ state.C.PyObject_GC_Track = rffi.llexternal(
+ 'PyObject_GC_Track', [rffi.VOIDP], lltype.Void,
+ compilation_info=eci, _nowrapper=True)
state.C._PyPy_tuple_traverse = rffi.llexternal(
'_PyPy_tuple_traverse', [PyObject, visitproc, rffi.VOIDP],
rffi.INT, compilation_info=eci, _nowrapper=True)
diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py
--- a/pypy/module/cpyext/listobject.py
+++ b/pypy/module/cpyext/listobject.py
@@ -8,7 +8,6 @@
from pypy.objspace.std.listobject import W_ListObject
from pypy.interpreter.error import oefmt
-
PyList_Check, PyList_CheckExact = build_type_checkers_flags("List")
@cpython_api([Py_ssize_t], PyObject)
@@ -30,6 +29,33 @@
w_list.convert_to_cpy_strategy(space)
return CPyListStrategy.unerase(w_list.lstorage)
+def list_traverse(space, w_list, visit, args):
+ from rpython.rlib.rawrefcount import PYOBJ_HDR_PTR
+ from pypy.module.cpyext.sequence import CPyListStrategy
+ from rpython.rtyper.lltypesystem import lltype, llmemory
+ from rpython.rlib.debug import debug_print
+ assert isinstance(w_list, W_ListObject)
+ obj = llmemory.cast_ptr_to_adr(w_list.lstorage)
+ objint = llmemory.cast_adr_to_int(obj, "forced")
+ if objint != 0:
+ cpy_strategy = space.fromcache(CPyListStrategy)
+ if not cpy_strategy.locked and cpy_strategy == w_list.strategy:
+ debug_print('rrc do traverse ', w_list)
+ index = 0
+ storage = CPyListStrategy.unerase(w_list.lstorage)
+ while index < storage._length:
+ pyobj = storage._elems[index]
+ pyobj_hdr = rffi.cast(PYOBJ_HDR_PTR, pyobj)
+ if pyobj_hdr:
+ vret = rffi.cast(lltype.Signed, visit(pyobj_hdr, args))
+ if vret:
+ return vret
+ index += 1
+ else:
+ debug_print('rrc no traverse', w_list, 'locked',
+ cpy_strategy.locked, 'strategy', w_list.strategy)
+ return 0
+
@cpython_api([rffi.VOIDP, Py_ssize_t, PyObject], lltype.Void,
error=CANNOT_FAIL)
def PyList_SET_ITEM(space, w_list, index, py_item):
"""Form of PyList_SetItem() without error checking. This is normally
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
@@ -77,9 +77,9 @@
state = space.fromcache(State)
return state.C._PyPy_subtype_dealloc
- def get_free(self, space):
+ def get_free(self, space, gc):
state = space.fromcache(State)
- return state.C.PyObject_Free
+ return state.C.PyObject_GC_Del if gc else state.C.PyObject_Free
def has_traverse(self, space):
return False
@@ -156,7 +156,7 @@
return tp_dealloc
if tp_free:
- def get_free(self, space):
+ def get_free(self, space, gc):
return tp_free
if tp_attach:
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -281,6 +281,7 @@
erase, unerase = rerased.new_erasing_pair("cpylist")
erase = staticmethod(erase)
unerase = staticmethod(unerase)
+ locked = False
def _check_index(self, index, length):
if index < 0:
diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c
--- a/pypy/module/cpyext/src/object.c
+++ b/pypy/module/cpyext/src/object.c
@@ -132,10 +132,17 @@
PyObject *
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
{
+ PyObject *obj;
+
if (PyType_IS_GC(type))
- return (PyObject*)_PyObject_GC_NewVar(type, nitems);
+ obj = (PyObject*)_PyObject_GC_NewVar(type, nitems);
else
- return (PyObject*)_PyObject_NewVar(type, nitems);
+ obj = (PyObject*)_PyObject_NewVar(type, nitems);
+
+ if (PyType_IS_GC(type))
+ _PyObject_GC_TRACK(obj);
+
+ return obj;
}
PyObject *
@@ -191,7 +198,7 @@
return (PyVarObject*)PyErr_NoMemory();
if (type->tp_itemsize == 0)
- return (PyVarObject*)PyObject_INIT(py_obj, type);
+ return PyObject_INIT(py_obj, type);
else
return PyObject_INIT_VAR((PyVarObject*)py_obj, type, nitems);
}
diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py
--- a/pypy/module/cpyext/state.py
+++ b/pypy/module/cpyext/state.py
@@ -133,9 +133,10 @@
print 'dealloc_trigger DONE'
return "RETRY"
def tp_traverse(pyobj_ptr, callback, args):
- from pypy.module.cpyext.api import PyObject
+ from pypy.module.cpyext.api import PyObject, generic_cpy_call
from pypy.module.cpyext.typeobjectdefs import visitproc
- from pypy.module.cpyext.pyobject import cts
+ from pypy.module.cpyext.pyobject import cts, from_ref
+
# convert to pointers with correct types (PyObject)
callback_addr = llmemory.cast_ptr_to_adr(callback)
callback_ptr = llmemory.cast_adr_to_ptr(callback_addr,
@@ -148,9 +149,9 @@
print 'traverse PyObject', pyobj, 'of type', name
# now call tp_traverse (if possible)
- if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse:
- pyobj.c_ob_type.c_tp_traverse(pyobj, callback_ptr,
- args)
+ if pto and pto.c_tp_traverse:
+ generic_cpy_call(space, pto.c_tp_traverse, pyobj,
+ callback_ptr, args)
rawrefcount.init(dealloc_trigger, tp_traverse)
else:
if space.config.translation.gc == "boehm":
@@ -176,9 +177,12 @@
(lambda w_obj: _clear_weakref_callbacks(w_obj))
def _tp_traverse(pyobj_ptr, callback, args):
- from pypy.module.cpyext.api import PyObject
+ from pypy.module.cpyext.api import PyObject, \
+ generic_cpy_call
from pypy.module.cpyext.typeobjectdefs import visitproc
- from pypy.module.cpyext.pyobject import cts
+ from pypy.module.cpyext.pyobject import cts, from_ref
+ from pypy.module.cpyext.listobject import list_traverse
+
# convert to pointers with correct types (PyObject)
callback_addr = llmemory.cast_ptr_to_adr(callback)
callback_ptr = llmemory.cast_adr_to_ptr(callback_addr,
@@ -208,9 +212,19 @@
debug_print(" " * i * 3, "unknown base")
pto2 = base
i += 1
+
+ if pyobj.c_ob_pypy_link != 0: # special traverse
+ w_obj = from_ref(space, pyobj)
+ w_obj_type = space.type(w_obj)
+ if space.is_w(w_obj_type, space.w_list): # list
+ debug_print('rrc list traverse ', pyobj)
+ list_traverse(space, w_obj, callback, args)
+ return
+
if pto and pto.c_tp_traverse:
debug_print("rrc do traverse", pyobj)
- pto.c_tp_traverse(pyobj, callback_ptr, args)
+ generic_cpy_call(space, pto.c_tp_traverse, pyobj,
+ callback_ptr, args)
self.tp_traverse = (lambda o, v, a:_tp_traverse(o, v, a))
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -434,7 +434,7 @@
pto.c_tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS
elif space.issubtype_w(w_obj, space.w_tuple):
pto.c_tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS
- pto.c_tp_flags |= Py_TPFLAGS_HAVE_GC
+ pto.c_tp_flags |= Py_TPFLAGS_HAVE_GC # TODO: remove? already done
elsewhere
elif space.issubtype_w(w_obj, space.w_list):
pto.c_tp_flags |= Py_TPFLAGS_LIST_SUBCLASS
elif space.issubtype_w(w_obj, space.w_dict):
@@ -610,6 +610,8 @@
elif space.is_w(w_type, space.w_tuple):
pto.c_tp_itemsize = rffi.sizeof(PyObject)
pto.c_tp_flags |= Py_TPFLAGS_HAVE_GC
+ elif space.is_w(w_type, space.w_list):
+ pto.c_tp_flags |= Py_TPFLAGS_HAVE_GC
state = space.fromcache(State)
pto.c_tp_alloc = state.C.PyType_GenericAlloc
@@ -646,13 +648,6 @@
# strange, but happens (ABCMeta)
pto.c_tp_dealloc = state.C._PyPy_subtype_dealloc
- # free
- if space.gettypeobject(w_type.layout.typedef) is w_type:
- # only for the exact type, like 'space.w_tuple' or 'space.w_list'
- pto.c_tp_free = typedescr.get_free(space)
- else:
- pto.c_tp_free = pto.c_tp_base.c_tp_free
-
from rpython.rtyper.lltypesystem import llmemory
from pypy.module.cpyext.typeobjectdefs import traverseproc
if typedescr.has_traverse(space):
@@ -685,6 +680,18 @@
if pto.c_tp_base != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
pto.c_tp_new = pto.c_tp_base.c_tp_new
decref(space, base_object_pyo)
+
+ # free
+ gc = pto.c_tp_flags & Py_TPFLAGS_HAVE_GC != 0
+ if space.gettypeobject(w_type.layout.typedef) is w_type:
+ # only for the exact type, like 'space.w_tuple' or 'space.w_list'
+ pto.c_tp_free = typedescr.get_free(space, gc)
+ else:
+ if gc:
+ pto.c_tp_free = state.C.PyObject_GC_Del
+ else:
+ pto.c_tp_free = state.C.PyObject_Free
+
pto.c_tp_flags |= Py_TPFLAGS_READY
return pto
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -248,9 +248,11 @@
cpy_strategy = self.space.fromcache(CPyListStrategy)
if self.strategy is cpy_strategy:
return
+ cpy_strategy.locked = True
lst = self.getitems()
self.strategy = cpy_strategy
self.lstorage = cpy_strategy.erase(CPyListStorage(space, lst))
+ cpy_strategy.locked = False
# ___________________________________________________
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -3383,6 +3383,14 @@
#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)
@@ -3434,7 +3442,7 @@
# TODO: check again, if some new border objects have been marked and
# continue marking recursively... why needed? -> wrapper for
- # pypy-obj is no pygc-obj??? ...
+ # pypy-obj is no pygc-obj??? ...KI
self._rrc_debug_check_consistency(print_label="end-mark")
#self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrc) #
TODO: remove?
@@ -3470,6 +3478,14 @@
self.visit_all_objects()
def rrc_major_collection_free(self):
+ if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
+ if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
+ self._rrc_debug_check_consistency()
+ 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()
@@ -3489,14 +3505,6 @@
self.rrc_o_list_old.delete()
self.rrc_o_list_old = new_o_list
- if self.rrc_state == self.RAWREFCOUNT_STATE_DEFAULT:
- if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list):
- self._rrc_debug_check_consistency()
- 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")
-
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
@@ -3564,13 +3572,13 @@
self._rrc_pyobj_gc_refcnt_set(pygchdr, refcnt)
pygchdr = pygchdr.c_gc_next
- self._rrc_debug_check_consistency(print_label="rc-initialized")
-
# 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)
+ self._rrc_debug_check_consistency(print_label="rc-initialized")
+
# Subtract all internal refcounts from the cyclic refcount
# of rawrefcounted objects
pygchdr = pygclist.c_gc_next
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit