[pypy-commit] pypy cpyext-gc-cycle: Implemented tuple untracking for rrc objects
stevie_92
pypy.commits at gmail.com
Fri Jun 21 16:06:20 EDT 2019
Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r96835:a1dc8b9e7d98
Date: 2019-06-21 22:05 +0200
http://bitbucket.org/pypy/pypy/changeset/a1dc8b9e7d98/
Log: Implemented tuple untracking for rrc objects Improved stability list
traversal and removed unneccesary debug output Fixed inheritance
issues with some cypthon slots
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
@@ -1216,6 +1216,15 @@
state.C._PyPy_init_pyobj_list = rffi.llexternal(
'_PyPy_init_pyobj_list', [], PyGC_HeadPtr,
compilation_info=eci, _nowrapper=True)
+ state.C._PyPy_init_tuple_list = rffi.llexternal(
+ '_PyPy_init_tuple_list', [], PyGC_HeadPtr,
+ compilation_info=eci, _nowrapper=True)
+ state.C._PyTuple_MaybeUntrack = rffi.llexternal(
+ '_PyTuple_MaybeUntrack', [PyObject], lltype.Signed,
+ compilation_info=eci, _nowrapper=True)
+ state.C._PyList_CheckExact = rffi.llexternal(
+ '_PyList_CheckExact', [PyObject], lltype.Signed,
+ compilation_info=eci, _nowrapper=True)
state.C._PyPy_gc_as_pyobj = rffi.llexternal(
'_PyPy_gc_as_pyobj', [PyGC_HeadPtr], GCHdr_PyObject,
compilation_info=eci, _nowrapper=True)
@@ -1348,7 +1357,9 @@
# initialize the pyobj_list for the gc
pyobj_list = space.fromcache(State).C._PyPy_init_pyobj_list()
+ pyobj_tuple_list = space.fromcache(State).C._PyPy_init_tuple_list()
rawrefcount._init_pyobj_list(pyobj_list)
+ rawrefcount._init_pyobj_list(pyobj_tuple_list)
# we need to call this *after* the init code above, because it might
# indirectly call some functions which are attached to pypyAPI (e.g., we
@@ -1552,6 +1563,7 @@
source_dir / "object.c",
source_dir / "typeobject.c",
source_dir / "tupleobject.c",
+ source_dir / "listobject.c",
]
def build_eci(code, use_micronumpy=False, translating=False):
diff --git a/pypy/module/cpyext/include/listobject.h b/pypy/module/cpyext/include/listobject.h
--- a/pypy/module/cpyext/include/listobject.h
+++ b/pypy/module/cpyext/include/listobject.h
@@ -1,4 +1,16 @@
-/* empty */
+#ifndef Py_LISTOBJECT_H
+#define Py_LISTOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define PyList_Check(op) \
PyType_FastSubclass((op)->ob_type, Py_TPFLAGS_LIST_SUBCLASS)
#define PyList_CheckExact(op) ((op)->ob_type == &PyList_Type)
+
+PyAPI_FUNC(Py_ssize_t) _PyList_CheckExact(PyObject *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_LISTOBJECT_H */
\ No newline at end of file
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -295,6 +295,7 @@
( (type *) _PyObject_GC_NewVar(typeobj, size) )
extern PyGC_Head *_pypy_rawrefcount_pyobj_list;
+extern PyGC_Head *_pypy_rawrefcount_tuple_list;
#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
#define _Py_FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
@@ -327,9 +328,15 @@
#define _PyGC_REFS_REACHABLE (-3)
#define _PyGC_REFS_TENTATIVELY_UNREACHABLE (-4)
+#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
+#define PyObject_IS_GC(o) \
+ (PyType_IS_GC(Py_TYPE(o)) \
+ && (Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o)))
+
#define _PyGC_IS_TRACKED(o) (_PyGC_REFS(o) != _PyGC_REFS_UNTRACKED)
-
-#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
+#define _PyObject_GC_MAY_BE_TRACKED(obj) \
+ (PyObject_IS_GC(obj) && \
+ (!PyTuple_CheckExact(obj) || _PyGC_IS_TRACKED(obj)))
PyAPI_FUNC(void) PyObject_GC_Track(void *);
PyAPI_FUNC(void) PyObject_GC_UnTrack(void *);
@@ -344,6 +351,16 @@
((PyGC_Head *)g->gc_prev)->gc_next = g; \
_pypy_rawrefcount_pyobj_list->gc_prev = g; \
} while(0)
+ #define _PyObject_GC_TRACK_Tuple(o) do { \
+ PyGC_Head *g = _Py_AS_GC(o); \
+ if (_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED) \
+ Py_FatalError("GC object already tracked"); \
+ _PyGCHead_SET_REFS(g, _PyGC_REFS_REACHABLE); \
+ g->gc_next = _pypy_rawrefcount_tuple_list; \
+ g->gc_prev = _pypy_rawrefcount_tuple_list->gc_prev; \
+ ((PyGC_Head *)g->gc_prev)->gc_next = g; \
+ _pypy_rawrefcount_tuple_list->gc_prev = g; \
+ } while(0)
#define _PyObject_GC_UNTRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
assert(_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED); \
@@ -438,6 +455,7 @@
PyAPI_FUNC(void) _PyPy_subtype_dealloc(PyObject *);
PyAPI_FUNC(void) _PyPy_object_dealloc(PyObject *);
PyAPI_FUNC(PyGC_Head *) _PyPy_init_pyobj_list();
+PyAPI_FUNC(PyGC_Head *) _PyPy_init_tuple_list();
PyAPI_FUNC(GCHdr_PyObject *) _PyPy_gc_as_pyobj(PyGC_Head *);
PyAPI_FUNC(PyGC_Head *) _PyPy_pyobj_as_gc(GCHdr_PyObject *);
PyAPI_FUNC(Py_ssize_t) _PyPy_finalizer_type(PyGC_Head *);
diff --git a/pypy/module/cpyext/include/tupleobject.h b/pypy/module/cpyext/include/tupleobject.h
--- a/pypy/module/cpyext/include/tupleobject.h
+++ b/pypy/module/cpyext/include/tupleobject.h
@@ -20,6 +20,7 @@
PyAPI_FUNC(void) _PyPy_tuple_dealloc(PyObject *);
PyAPI_FUNC(void) _PyPy_tuple_free(void *);
PyAPI_FUNC(int) _PyPy_tuple_traverse(PyObject *ob, visitproc visit, void *arg);
+PyAPI_FUNC(Py_ssize_t) _PyTuple_MaybeUntrack(PyObject *);
/* defined in varargswrapper.c */
PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
diff --git a/pypy/module/cpyext/src/listobject.c b/pypy/module/cpyext/src/listobject.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/listobject.c
@@ -0,0 +1,7 @@
+#include "Python.h"
+
+Py_ssize_t
+_PyList_CheckExact(PyObject *op)
+{
+ return op->ob_type == &PyList_Type;
+}
\ No newline at end of file
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
@@ -23,7 +23,10 @@
void* _pypy_rawrefcount_w_marker_deallocating = (void*) 0xDEADFFF;
static PyGC_Head _internal_pyobj_list;
+static PyGC_Head _internal_tuple_list;
+
PyGC_Head *_pypy_rawrefcount_pyobj_list = &_internal_pyobj_list;
+PyGC_Head *_pypy_rawrefcount_tuple_list = &_internal_tuple_list;
PyGC_Head *
_PyPy_init_pyobj_list()
@@ -33,6 +36,14 @@
return _pypy_rawrefcount_pyobj_list;
}
+PyGC_Head *
+_PyPy_init_tuple_list()
+{
+ _pypy_rawrefcount_tuple_list->gc_next = _pypy_rawrefcount_tuple_list;
+ _pypy_rawrefcount_tuple_list->gc_prev = _pypy_rawrefcount_tuple_list;
+ return _pypy_rawrefcount_tuple_list;
+}
+
GCHdr_PyObject *
_PyPy_gc_as_pyobj(PyGC_Head *g)
{
diff --git a/pypy/module/cpyext/src/tupleobject.c b/pypy/module/cpyext/src/tupleobject.c
--- a/pypy/module/cpyext/src/tupleobject.c
+++ b/pypy/module/cpyext/src/tupleobject.c
@@ -56,7 +56,7 @@
}
for (i=0; i < size; i++)
op->ob_item[i] = NULL;
- _PyObject_GC_TRACK(op);
+ _PyObject_GC_TRACK_Tuple(op);
return (PyObject *) op;
}
@@ -105,4 +105,33 @@
for (i = Py_SIZE(o); --i >= 0; )
Py_VISIT(o->ob_item[i]);
return 0;
+}
+
+/* Return 0 if the tuple is untracked afterwards, return 1 if the tuple
+ should always be kept tracked and return 2 if the tuple was not fully
+ intialized yet. */
+Py_ssize_t
+_PyTuple_MaybeUntrack(PyObject *op)
+{
+ PyTupleObject *t;
+ Py_ssize_t i, n;
+
+ if (!PyTuple_CheckExact(op))
+ return 1;
+ if (!_PyGC_IS_TRACKED(op))
+ return 0;
+ t = (PyTupleObject *) op;
+ n = Py_SIZE(t);
+ for (i = 0; i < n; i++) {
+ PyObject *elt = PyTuple_GET_ITEM(t, i);
+ /* Tuple with NULL elements aren't
+ fully constructed, don't untrack
+ them yet. */
+ if (!elt)
+ return 2;
+ if (_PyObject_GC_MAY_BE_TRACKED(elt))
+ return 1;
+ }
+ _PyObject_GC_UNTRACK(op);
+ return 0;
}
\ No newline at end of file
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
@@ -184,47 +184,28 @@
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,
- visitproc)
pyobj_addr = llmemory.cast_ptr_to_adr(pyobj_ptr)
pyobj = llmemory.cast_adr_to_ptr(pyobj_addr, PyObject)
+
# now call tp_traverse (if possible)
debug_print("rrc check traverse", pyobj)
- pto = pyobj.c_ob_type
- if pto and pto.c_tp_name:
- tp_name = pto.c_tp_name
- name = rffi.charp2str(cts.cast('char*', tp_name))
- debug_print("rrc try traverse", pyobj, ": type", pto,
- ": name", name)
- pto2 = pto
- i = 1
- while pto2.c_tp_base:
- base = pto2.c_tp_base
- if base.c_tp_name:
- tp_name = base.c_tp_name
- name = rffi.charp2str(cts.cast('char*', tp_name))
- debug_print(" " * i * 3, "basetype",
- base, ": name",
- name, "traverse",
- base.c_tp_traverse)
- else:
- 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)
- generic_cpy_call(space, pto.c_tp_traverse, pyobj,
- callback_ptr, args)
+ # special traverse for list
+ if self.C._PyList_CheckExact(pyobj) != 0:
+ if pyobj.c_ob_pypy_link != 0:
+ w_obj = from_ref(space, pyobj)
+ if w_obj:
+ debug_print('rrc list traverse ', pyobj)
+ list_traverse(space, w_obj, callback, args)
+ else:
+ pto = pyobj.c_ob_type
+ if pto and pto.c_tp_traverse:
+ callback_addr = llmemory.cast_ptr_to_adr(callback)
+ callback_ptr = llmemory.cast_adr_to_ptr(
+ callback_addr, visitproc)
+ debug_print("rrc do traverse", pyobj)
+ generic_cpy_call(space, pto.c_tp_traverse, pyobj,
+ callback_ptr, args)
self.tp_traverse = (lambda o, v, a:_tp_traverse(o, v, a))
@@ -258,16 +239,18 @@
# This must be called in RPython, the untranslated version
# does something different. Sigh.
pypyobj_list = self.C._PyPy_init_pyobj_list()
+ pypyobj_tuple_list = self.C._PyPy_init_tuple_list()
rawrefcount.init(
llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER,
self.dealloc_trigger),
llhelper(rawrefcount.RAWREFCOUNT_TRAVERSE,
self.tp_traverse),
- pypyobj_list,
+ pypyobj_list, pypyobj_tuple_list,
self.C._PyPy_gc_as_pyobj, self.C._PyPy_pyobj_as_gc,
self.C._PyPy_finalizer_type,
llhelper(rawrefcount.RAWREFCOUNT_CLEAR_WR_TYPE,
- self.clear_weakref_callbacks))
+ self.clear_weakref_callbacks),
+ self.C._PyTuple_MaybeUntrack)
self.builder.attach_all(space)
setup_new_method_def(space)
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
@@ -740,6 +740,10 @@
pto.c_tp_clear = base.c_tp_clear
if not pto.c_tp_traverse:
pto.c_tp_traverse = base.c_tp_traverse
+ if not pto.c_tp_is_gc:
+ pto.c_tp_is_gc = base.c_tp_is_gc
+ if not pto.c_tp_finalize:
+ pto.c_tp_finalize = base.c_tp_finalize
# XXX check for correct GC flags!
if not pto.c_tp_free:
pto.c_tp_free = base.c_tp_free
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
@@ -3114,6 +3114,8 @@
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
@@ -3127,8 +3129,9 @@
return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR)
def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse,
- pyobj_list, gc_as_pyobj, pyobj_as_gc, finalizer_type,
- clear_weakref_callback):
+ 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()
@@ -3141,6 +3144,7 @@
self.rrc_dealloc_pending = self.AddressStack()
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()
@@ -3150,6 +3154,7 @@
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
self.rrc_enabled = True
self.rrc_cycle_enabled = True
self.rrc_state = self.RAWREFCOUNT_STATE_DEFAULT
@@ -3226,6 +3231,10 @@
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))
+# if not self._rrc_gc_list_is_empty(self.rrc_tuple_isolate_list):
+# gchdr = self._rrc_gc_list_pop(self.rrc_tuple_isolate_list)
+# self._rrc_gc_list_add(self.rrc_tuple_old_list, gchdr)
+# return llmemory.cast_ptr_to_adr(self.rrc_gc_as_pyobj(gchdr))
return llmemory.NULL
def rawrefcount_cyclic_garbage_head(self):
@@ -3288,6 +3297,8 @@
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_tuple_isolate_list) or
not self._rrc_gc_list_is_empty(
self.rrc_pyobj_dead_list) or
not self._rrc_gc_list_is_empty(
@@ -3415,6 +3426,10 @@
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
@@ -3424,7 +3439,7 @@
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_rawrefcount_roots(self.rrc_pyobj_list)
+ 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
@@ -3449,13 +3464,7 @@
# 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)
-
- # 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??? ...KI
-
self._rrc_debug_check_consistency(print_label="end-mark")
- #self.rrc_o_list_old.foreach(self._rrc_major_trace, use_cylicrc) # TODO: remove?
def _rrc_major_trace(self, pyobject, use_cylicrefcnt):
from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
@@ -3513,12 +3522,12 @@
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_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")
+ 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()
@@ -3543,12 +3552,26 @@
# 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.
- gchdr = self.rrc_pyobj_old_list.c_gc_next
- while gchdr <> self.rrc_pyobj_old_list:
+ 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
#
@@ -3591,11 +3614,31 @@
else:
self._rrc_free(pyobject, True)
- def _rrc_collect_rawrefcount_roots(self, pygclist):
+ def _rrc_collect_roots(self, pygclist):
+ # Initialize the cyclic refcount with the real refcount.
+ self._rrc_collect_roots_init_list(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)
+
+ # Subtract all internal refcounts from the cyclic refcount
+ # of rawrefcounted objects
+ self._rrc_collect_roots_subtract_internal(pygclist)
+
+ # 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
- #
- # Initialize the cyclic refcount with the real refcount.
pygchdr = pygclist.c_gc_next
while pygchdr <> pygclist:
refcnt = self.rrc_gc_as_pyobj(pygchdr).c_ob_refcnt
@@ -3606,28 +3649,13 @@
self._rrc_pyobj_gc_refcnt_set(pygchdr, refcnt)
pygchdr = pygchdr.c_gc_next
- # 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)
-
- self._rrc_debug_check_consistency(print_label="rc-initialized")
-
- # Subtract all internal refcounts from the cyclic refcount
- # of rawrefcounted objects
+ 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
- # now all rawrefcounted roots or live border objects have a
- # refcount > 0
-
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
@@ -3662,49 +3690,54 @@
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)
- return
+ 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
- self._rrc_gc_list_move(self.rrc_pyobj_list, self.rrc_pyobj_old_list)
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
- 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)
- 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
- self._rrc_gc_list_add(self.rrc_pyobj_list, 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()
- found_alive = True
+ 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)
+ if pyobj.c_ob_pypy_link <> 0:
+ intobj = pyobj.c_ob_pypy_link
+ obj = llmemory.cast_int_to_adr(intobj)
+ 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
@@ -3788,7 +3821,7 @@
# Check, if the cyclic isolate from the last collection cycle
# is reachable from outside, after the finalizers have been
# executed.
- self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_old_list)
+ self._rrc_collect_roots(self.rrc_pyobj_old_list)
found_alive = False
gchdr = self.rrc_pyobj_old_list.c_gc_next
while gchdr <> self.rrc_pyobj_old_list:
@@ -3899,6 +3932,8 @@
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,
diff --git a/rpython/memory/gc/test/dot/free_cpython_simple.dot b/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot
copy from rpython/memory/gc/test/dot/free_cpython_simple.dot
copy to rpython/memory/gc/test/dot/free_cpython_tuple_1.dot
--- a/rpython/memory/gc/test/dot/free_cpython_simple.dot
+++ b/rpython/memory/gc/test/dot/free_cpython_tuple_1.dot
@@ -1,5 +1,5 @@
digraph G {
- "a" [type=C, alive=n];
+ "a" [type=C, alive=n, tuple=y];
"b" [type=C, alive=n];
"a" -> "b";
"b" -> "a";
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
@@ -78,17 +78,32 @@
weakref.callback_cleared = True
cleared = True
+ def rawrefcount_tuple_maybe_untrack(obj):
+ #if foo:
+ # gchdr = rawrefcount_pyobj_as_gc(obj)
+ # next = gchdr.c_gc_next
+ # next.c_gc_prev = gchdr.c_gc_prev
+ # gchdr.c_gc_prev.c_gc_next = next
+ # return 0
+ return 1 # TODO: add tests for 0 ("plain" tuple) and 2 (uninitialized)
+
self.pyobj_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw',
immortal=True)
self.pyobj_list.c_gc_next = self.pyobj_list
self.pyobj_list.c_gc_prev = self.pyobj_list
+ self.tuple_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw',
+ immortal=True)
+ self.tuple_list.c_gc_next = self.tuple_list
+ self.tuple_list.c_gc_prev = self.tuple_list
self.gc.rawrefcount_init(lambda: self.trigger.append(1),
rawrefcount_tp_traverse,
llmemory.cast_ptr_to_adr(self.pyobj_list),
+ llmemory.cast_ptr_to_adr(self.tuple_list),
rawrefcount_gc_as_pyobj,
rawrefcount_pyobj_as_gc,
rawrefcount_finalizer_type,
- rawrefcount_clear_wr)
+ rawrefcount_clear_wr,
+ rawrefcount_tuple_maybe_untrack)
def _collect(self, major, expected_trigger=0):
if major:
@@ -138,7 +153,7 @@
return p1, p1ref, check_alive
def _rawrefcount_pyobj(self, create_immortal=False, is_gc=True,
- tracked=True):
+ tracked=True, tuple=tuple):
r1 = lltype.malloc(PYOBJ_HDR, flavor='raw',
immortal=create_immortal)
r1.c_ob_refcnt = 0
@@ -146,7 +161,7 @@
r1addr = llmemory.cast_ptr_to_adr(r1)
if is_gc:
- self._rawrefcount_add_gc(tracked)
+ self._rawrefcount_add_gc(tracked, tuple)
self.pyobjs.append(r1)
self.is_pygc.append(is_gc)
@@ -161,7 +176,7 @@
def _rawrefcount_pair(self, intval, is_light=False, is_pyobj=False,
create_old=False, create_immortal=False,
rooted=False, force_external=False, is_gc=True,
- tracked=True):
+ tracked=True, tuple=tuple):
if is_light:
rc = REFCNT_FROM_PYPY_LIGHT
else:
@@ -194,7 +209,7 @@
r1addr = llmemory.cast_ptr_to_adr(r1)
if is_gc:
- self._rawrefcount_add_gc(tracked)
+ self._rawrefcount_add_gc(tracked, tuple)
self.pyobjs.append(r1)
self.is_pygc.append(is_gc)
@@ -222,16 +237,22 @@
return p1
return p1, p1ref, r1, r1addr, check_alive
- def _rawrefcount_add_gc(self, tracked):
+ def _rawrefcount_add_gc(self, tracked, tuple):
r1gc = lltype.malloc(PYOBJ_GC_HDR, flavor='raw',
immortal=True)
self.gcobjs.append(r1gc)
if tracked:
r1gc.c_gc_refs = 0
- r1gc.c_gc_next = self.pyobj_list
- r1gc.c_gc_prev = self.pyobj_list.c_gc_prev
- r1gc.c_gc_prev.c_gc_next = r1gc
- self.pyobj_list.c_gc_prev = r1gc
+ if tuple:
+ r1gc.c_gc_next = self.tuple_list
+ r1gc.c_gc_prev = self.tuple_list.c_gc_prev
+ r1gc.c_gc_prev.c_gc_next = r1gc
+ self.tuple_list.c_gc_prev = r1gc
+ else:
+ r1gc.c_gc_next = self.pyobj_list
+ r1gc.c_gc_prev = self.pyobj_list.c_gc_prev
+ r1gc.c_gc_prev.c_gc_next = r1gc
+ self.pyobj_list.c_gc_prev = r1gc
else:
r1gc.c_gc_refs = RAWREFCOUNT_REFS_UNTRACKED
@@ -485,7 +506,7 @@
class NodeInfo:
def __init__(self, type, alive, ext_refcnt, finalizer, resurrect,
- delete, garbage):
+ delete, garbage, tuple):
self.type = type
self.alive = alive
self.ext_refcnt = ext_refcnt
@@ -493,6 +514,7 @@
self.resurrect = resurrect
self.delete = delete
self.garbage = garbage
+ self.tuple = tuple
class WeakrefNode(BorderNode):
def __init__(self, p, pref, r, raddr, check_alive, info, r_dest,
@@ -529,11 +551,12 @@
resurrect = attr['resurrect'] if 'resurrect' in attr else None
delete = attr['delete'] if 'delete' in attr else None
garbage = True if 'garbage' in attr else False
+ tuple = attr['tuple'] == "y" if 'tuple' in attr else False
info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect,
- delete, garbage)
+ delete, garbage, tuple)
if type == "C":
r, raddr, check_alive = self._rawrefcount_pyobj(
- tracked=tracked)
+ tracked=tracked, tuple=tuple)
r.c_ob_refcnt += ext_refcnt
nodes[name] = CPythonNode(r, raddr, check_alive, info)
elif type == "P":
@@ -545,7 +568,8 @@
elif type == "B":
p, pref, r, raddr, check_alive =\
self._rawrefcount_pair(42 + i, rooted=rooted,
- create_old=True, tracked=tracked)
+ create_old=True, tracked=tracked,
+ tuple=tuple)
r.c_ob_refcnt += ext_refcnt
nodes[name] = BorderNode(p, pref, r, raddr, check_alive, info)
i += 1
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
@@ -483,11 +483,13 @@
self.rawrefcount_init_ptr = getfn(
GCClass.rawrefcount_init,
[s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER),
- SomePtr(GCClass.RAWREFCOUNT_TRAVERSE), SomeAddress(),
+ SomePtr(GCClass.RAWREFCOUNT_TRAVERSE),
+ SomeAddress(), SomeAddress(),
SomePtr(GCClass.RAWREFCOUNT_GC_AS_PYOBJ),
SomePtr(GCClass.RAWREFCOUNT_PYOBJ_AS_GC),
SomePtr(GCClass.RAWREFCOUNT_FINALIZER_TYPE),
- SomePtr(GCClass.RAWREFCOUNT_CLEAR_WR_TYPE)],
+ SomePtr(GCClass.RAWREFCOUNT_CLEAR_WR_TYPE),
+ SomePtr(GCClass.RAWREFCOUNT_MAYBE_UNTRACK_TUPLE)],
annmodel.s_None)
self.rawrefcount_create_link_pypy_ptr = getfn(
GCClass.rawrefcount_create_link_pypy,
@@ -1365,8 +1367,8 @@
self.pop_roots(hop, livevars)
def gct_gc_rawrefcount_init(self, hop):
- [v_fnptr, v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4,
- v_fnptr5, v_fnptr6] = hop.spaceop.args
+ [v_fnptr, v_fnptr2, v_pyobj_list, v_tuple_list, v_fnptr3, v_fnptr4,
+ v_fnptr5, v_fnptr6, v_fnptr7] = hop.spaceop.args
assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER
assert v_fnptr2.concretetype == self.GCClass.RAWREFCOUNT_TRAVERSE
# TODO add assert for v_pyobj_list, improve asserts (types not same but equal)
@@ -1374,8 +1376,8 @@
# assert v_fnptr4.concretetype == self.GCClass.RAWREFCOUNT_PYOBJ_AS_GC
hop.genop("direct_call",
[self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr,
- v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4, v_fnptr5,
- v_fnptr6])
+ v_fnptr2, v_pyobj_list, v_tuple_list, v_fnptr3, v_fnptr4,
+ v_fnptr5, v_fnptr6, v_fnptr7])
def gct_gc_rawrefcount_create_link_pypy(self, hop):
[v_gcobj, v_pyobject] = hop.spaceop.args
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -308,8 +308,10 @@
_about_ = init
def compute_result_annotation(self, s_dealloc_callback, s_tp_traverse,
- s_pyobj_list, s_as_gc, s_as_pyobj,
- a_finalizer_type, a_clear_wr):
+ s_pyobj_list, v_tuple_list,
+ s_as_gc, s_as_pyobj,
+ a_finalizer_type, a_clear_wr,
+ a_maybe_untrack_tuple):
from rpython.rtyper.llannotation import SomePtr
assert isinstance(s_dealloc_callback, SomePtr) # ll-ptr-to-function
assert isinstance(s_tp_traverse, SomePtr)
@@ -317,14 +319,18 @@
assert isinstance(s_as_pyobj, SomePtr)
assert isinstance(a_finalizer_type, SomePtr)
assert isinstance(a_clear_wr, SomePtr)
+ assert isinstance(a_maybe_untrack_tuple, SomePtr)
def specialize_call(self, hop):
hop.exception_cannot_occur()
- v_dealloc_callback, v_tp_traverse, v_pyobj_list, v_as_gc, \
- v_as_pyobj, v_finalizer_type, v_clear_wr = hop.inputargs(*hop.args_r)
+ v_dealloc_callback, v_tp_traverse, v_pyobj_list, v_tuple_list, \
+ v_as_gc, v_as_pyobj, v_finalizer_type, \
+ v_clear_wr, v_maybe_untrack_tuple = hop.inputargs(*hop.args_r)
hop.genop('gc_rawrefcount_init', [v_dealloc_callback, v_tp_traverse,
- v_pyobj_list, v_as_gc, v_as_pyobj,
- v_finalizer_type, v_clear_wr])
+ v_pyobj_list, v_tuple_list,
+ v_as_gc, v_as_pyobj,
+ v_finalizer_type, v_clear_wr,
+ v_maybe_untrack_tuple])
class Entry(ExtRegistryEntry):
More information about the pypy-commit
mailing list