[pypy-commit] pypy cpyext-gc-cycle: Added additional flags for objects
stevie_92
pypy.commits at gmail.com
Fri Jan 11 05:38:55 EST 2019
Author: Stefan Beyer <home at sbeyer.at>
Branch: cpyext-gc-cycle
Changeset: r95598:c03fe327893a
Date: 2018-03-19 10:52 +0100
http://bitbucket.org/pypy/pypy/changeset/c03fe327893a/
Log: Added additional flags for objects Implemented refcount overhead
(for non-cyclic refcount) Implemented buffer for potential roots of
cycles Fixed assert to allow for recursive cpyext calls Added some
cycle detection tests from experimental branch (disabled now)
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
@@ -208,10 +208,11 @@
# running and should not themselves release the GIL).
#
# **make_generic_cpy_call():** RPython to C, with the GIL held. Before
-# the call, must assert that the global variable is 0 and set the
-# current thread identifier into the global variable. After the call,
-# assert that the global variable still contains the current thread id,
-# and reset it to 0.
+# the call, must assert that the global variable is 0 or the current
+# thread identifier (recursive call) and set the current thread identifier
+# into the global variable. After the call, assert that the global variable
+# still contains the current thread id, and reset it to the value it held
+# before the call.
#
# **make_wrapper():** C to RPython; by default assume that the GIL is
# held, but accepts gil="acquire", "release", "around",
@@ -1763,7 +1764,8 @@
# see "Handling of the GIL" above
tid = rthread.get_ident()
- assert cpyext_glob_tid_ptr[0] == 0
+ tid_before = cpyext_glob_tid_ptr[0]
+ assert tid_before == 0 or tid_before == tid
cpyext_glob_tid_ptr[0] = tid
preexist_error = PyErr_Occurred(space)
@@ -1772,7 +1774,7 @@
result = call_external_function(func, *boxed_args)
finally:
assert cpyext_glob_tid_ptr[0] == tid
- cpyext_glob_tid_ptr[0] = 0
+ cpyext_glob_tid_ptr[0] = tid_before
for i, ARG in unrolling_arg_types:
# note that this loop is nicely unrolled statically by RPython
_pyobj = to_decref[i]
diff --git a/pypy/module/cpyext/include/boolobject.h b/pypy/module/cpyext/include/boolobject.h
--- a/pypy/module/cpyext/include/boolobject.h
+++ b/pypy/module/cpyext/include/boolobject.h
@@ -13,8 +13,8 @@
#define Py_True ((PyObject *) &_Py_TrueStruct)
/* Macros for returning Py_True or Py_False, respectively */
-#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
-#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#define Py_RETURN_TRUE do { Py_INCREF(Py_True); return Py_True; } while(0)
+#define Py_RETURN_FALSE do { Py_INCREF(Py_False); return Py_False; } while(0)
#ifdef __cplusplus
}
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
@@ -2,6 +2,7 @@
#define Py_OBJECT_H
#include <stdio.h>
+#include <math.h>
#ifdef __cplusplus
extern "C" {
@@ -12,7 +13,13 @@
#define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1))
#define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1)
-#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#define PY_REFCNT_FROM_PYPY (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 2)))
+#define PY_REFCNT_GREEN (4L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 7)))
+#define PY_REFCNT_OVERFLOW (1L << ((long)(log(PY_SSIZE_T_MAX) / log(2) - 7) / 2L - 1L))
+#define PY_REFCNT_MASK ((PY_REFCNT_OVERFLOW << 1L) - 1L)
+#define Py_RETURN_NONE return (((((PyObject *)(Py_None))->ob_refcnt & PY_REFCNT_OVERFLOW) == 0) ? \
+ ((PyObject *)(Py_None))->ob_refcnt++ : Py_IncRef((PyObject *)(Py_None))), Py_None
+
/*
CPython has this for backwards compatibility with really old extensions, and now
@@ -34,14 +41,21 @@
#define Py_XDECREF(ob) (Py_DecRef((PyObject *)(ob)))
#else
/* Fast version */
-#define Py_INCREF(ob) (((PyObject *)(ob))->ob_refcnt++)
-#define Py_DECREF(op) \
- do { \
- if (--((PyObject *)(op))->ob_refcnt != 0) \
- ; \
- else \
- _Py_Dealloc((PyObject *)(op)); \
- } while (0)
+#define Py_INCREF(ob) do { \
+ if (!(((PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW)) \
+ ((PyObject *)(ob))->ob_refcnt++; \
+ else \
+ Py_IncRef((PyObject *)(ob)); \
+ } while (0)
+#define Py_DECREF(ob) do { \
+ if (!(((PyObject *)(ob))->ob_refcnt & PY_REFCNT_GREEN) || \
+ (((PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW)) \
+ Py_DecRef((PyObject *)(ob)); \
+ else if (--((PyObject *)(ob))->ob_refcnt & PY_REFCNT_MASK) \
+ ; \
+ else if ((!((PyObject *)(ob))->ob_refcnt) & PY_REFCNT_FROM_PYPY) \
+ _Py_Dealloc((PyObject *)(ob)); \
+ } while (0)
#define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0)
#define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0)
@@ -61,7 +75,8 @@
} \
} while (0)
-#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
+#define Py_REFCNT(ob) ((((PyObject *)(ob))->ob_refcnt & PY_REFCNT_OVERFLOW == 0) ? \
+ (((PyObject*)(ob))->ob_refcnt & PY_REFCNT_MASK) : _Py_RefCnt_Overflow(ob))
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -11,6 +11,7 @@
from pypy.module.cpyext.pyerrors import PyErr_NoMemory, PyErr_BadInternalCall
from pypy.objspace.std.typeobject import W_TypeObject
from pypy.interpreter.error import OperationError, oefmt
+from rpython.rlib.rawrefcount import REFCNT_MASK
import pypy.module.__builtin__.operation as operation
@@ -50,7 +51,7 @@
def _dealloc(space, obj):
# This frees an object after its refcount dropped to zero, so we
# assert that it is really zero here.
- assert obj.c_ob_refcnt == 0
+ assert obj.c_ob_refcnt & REFCNT_MASK == 0
pto = obj.c_ob_type
obj_voidp = rffi.cast(rffi.VOIDP, obj)
generic_cpy_call(space, pto.c_tp_free, obj_voidp)
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
@@ -17,8 +17,13 @@
from rpython.rlib.objectmodel import keepalive_until_here
from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_base_ptr
from rpython.rlib import rawrefcount, jit
-from rpython.rlib.debug import ll_assert, fatalerror
-
+from rpython.rlib.debug import ll_assert, fatalerror, debug_print
+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 pypy.module.cpyext.api import slot_function
+from pypy.module.cpyext.typeobjectdefs import visitproc
#________________________________________________________
# type description
@@ -249,8 +254,6 @@
w_obj._cpyext_attach_pyobj(space, py_obj)
-w_marker_deallocating = W_Root()
-
@jit.dont_look_inside
def from_ref(space, ref):
"""
@@ -262,7 +265,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"
@@ -315,7 +318,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 w_obj_has_pyobj(w_obj):
return bool(rawrefcount.from_obj(PyObject, w_obj))
@@ -341,7 +344,7 @@
pyobj = as_pyobj(space, w_obj, w_userdata, immortal=immortal)
if pyobj: # != NULL
assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
- pyobj.c_ob_refcnt += 1
+ rawrefcount.incref(pyobj)
keepalive_until_here(w_obj)
return pyobj
@@ -375,7 +378,7 @@
pyobj = rffi.cast(PyObject, pyobj)
w_obj = from_ref(space, pyobj)
if pyobj:
- pyobj.c_ob_refcnt -= 1
+ rawrefcount.decref(pyobj)
assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
keepalive_until_here(w_obj)
return w_obj
@@ -386,7 +389,7 @@
assert is_pyobj(pyobj)
pyobj = rffi.cast(PyObject, pyobj)
assert pyobj.c_ob_refcnt >= 1
- pyobj.c_ob_refcnt += 1
+ rawrefcount.incref(pyobj)
@specialize.ll()
def decref(space, pyobj):
@@ -394,23 +397,64 @@
assert is_pyobj(pyobj)
pyobj = rffi.cast(PyObject, pyobj)
if pyobj:
- assert pyobj.c_ob_refcnt > 0
- assert (pyobj.c_ob_pypy_link == 0 or
- pyobj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY)
- pyobj.c_ob_refcnt -= 1
- if pyobj.c_ob_refcnt == 0:
- state = space.fromcache(State)
- generic_cpy_call(space, state.C._Py_Dealloc, pyobj)
+ rawrefcount.decref(pyobj)
+ rc = pyobj.c_ob_refcnt
+ if rc & REFCNT_MASK == 0:
+ if rc & REFCNT_FROM_PYPY == 0 and rc & REFCNT_CLR_MASK != REFCNT_CLR_PURPLE:
+ state = space.fromcache(State)
+ generic_cpy_call(space, state.C._Py_Dealloc, pyobj)
+ elif rc & REFCNT_CLR_MASK != REFCNT_CLR_GREEN:
+ possible_root(space, pyobj)
#else:
# w_obj = rawrefcount.to_obj(W_Root, ref)
# if w_obj is not None:
# assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
+ at jit.dont_look_inside
+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:
+ #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
+
+ at cpython_api([PyObject], lltype.Void)
+def Py_IncRef(space, obj):
+ incref(space, obj)
+
+ at cpython_api([PyObject], lltype.Void)
+def Py_DecRef(space, obj):
+ decref(space, obj)
+
+ at cpython_api([PyObject], lltype.SignedLongLong, error=CANNOT_FAIL)
+def _Py_RefCnt_Overflow(space, obj):
+ return refcnt_overflow(space, obj)
+
+ at specialize.ll()
+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
@init_function
def write_w_marker_deallocating(space):
if we_are_translated():
- llptr = cast_instance_to_base_ptr(w_marker_deallocating)
+ llptr = cast_instance_to_base_ptr(W_MARKER_DEALLOCATING)
state = space.fromcache(State)
state.C.set_marker(rffi.cast(Py_ssize_t, llptr))
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
@@ -5,6 +5,7 @@
extern void _PyPy_Free(void *ptr);
extern void *_PyPy_Malloc(Py_ssize_t size);
+/*
void
Py_IncRef(PyObject *o)
{
@@ -16,6 +17,7 @@
{
Py_XDECREF(o);
}
+*/
/*
* The actual value of this variable will be the address of
diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py
--- a/pypy/module/cpyext/test/test_bytesobject.py
+++ b/pypy/module/cpyext/test/test_bytesobject.py
@@ -9,10 +9,12 @@
PyString_ConcatAndDel, PyString_Format, PyString_InternFromString,
PyString_AsEncodedObject, PyString_AsDecodedObject, _PyString_Eq,
_PyString_Join)
-from pypy.module.cpyext.api import PyObjectP, PyObject, Py_ssize_tP, generic_cpy_call
-from pypy.module.cpyext.pyobject import decref, from_ref, make_ref
+from pypy.module.cpyext.api import (
+ PyObjectP, PyObject, Py_ssize_tP, generic_cpy_call)
+from pypy.module.cpyext.pyobject import (
+ Py_DecRef, Py_IncRef, _Py_RefCnt_Overflow, from_ref, make_ref, decref)
from pypy.module.cpyext.buffer import PyObject_AsCharBuffer
-from pypy.module.cpyext.api import PyTypeObjectPtr
+from rpython.rlib import rawrefcount
class AppTestBytesObject(AppTestCpythonExtensionBase):
@@ -510,9 +512,9 @@
ref = make_ref(space, space.wrap('abc'))
ptr = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
ptr[0] = ref
- prev_refcnt = ref.c_ob_refcnt
+ prev_refcnt = ref.c_ob_refcnt & rawrefcount.REFCNT_MASK
PyString_Concat(space, ptr, space.wrap('def'))
- assert ref.c_ob_refcnt == prev_refcnt - 1
+ assert ref.c_ob_refcnt & rawrefcount.REFCNT_MASK == prev_refcnt - 1
assert space.str_w(from_ref(space, ptr[0])) == 'abcdef'
with pytest.raises(OperationError):
PyString_Concat(space, ptr, space.w_None)
@@ -548,9 +550,9 @@
w_text = space.wrap("text")
ref = make_ref(space, w_text)
- prev_refcnt = ref.c_ob_refcnt
+ prev_refcnt = ref.c_ob_refcnt & rawrefcount.REFCNT_MASK
assert PyObject_AsCharBuffer(space, ref, bufp, lenp) == 0
- assert ref.c_ob_refcnt == prev_refcnt
+ assert ref.c_ob_refcnt & rawrefcount.REFCNT_MASK == prev_refcnt
assert lenp[0] == 4
assert rffi.charp2str(bufp[0]) == 'text'
lltype.free(bufp, flavor='raw')
@@ -609,3 +611,53 @@
w_seq = space.wrap(['a', 'b'])
w_joined = _PyString_Join(space, w_sep, w_seq)
assert space.unwrap(w_joined) == 'a<sep>b'
+
+ def test_refcnt_overflow(self, space):
+ ref1 = make_ref(space, space.wrap('foo'))
+ ref1.c_ob_refcnt = rawrefcount.REFCNT_OVERFLOW - 1
+
+ Py_IncRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW
+
+ Py_IncRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+
+ Py_IncRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW + 2
+
+ Py_IncRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW + 3
+
+ Py_DecRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW + 2
+
+ Py_DecRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW + 1
+
+ Py_DecRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW
+ assert _Py_RefCnt_Overflow(space, ref1) \
+ == rawrefcount.REFCNT_OVERFLOW
+
+ Py_DecRef(space, ref1)
+ assert ref1.c_ob_refcnt & rawrefcount.REFCNT_MASK \
+ == rawrefcount.REFCNT_OVERFLOW - 1
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
@@ -74,6 +74,8 @@
from rpython.rlib.objectmodel import specialize
from rpython.rlib import rgc
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:
@@ -188,6 +190,16 @@
('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))
# ____________________________________________________________
@@ -2990,13 +3002,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
@@ -3005,6 +3017,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
@@ -3026,7 +3039,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):
@@ -3046,14 +3059,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_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_buffer_pyobj(self, pyobject):
+ self.rrc_buffered.append(pyobject)
def rawrefcount_from_obj(self, gcobj):
obj = llmemory.cast_ptr_to_adr(gcobj)
@@ -3064,7 +3080,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):
@@ -3085,15 +3101,14 @@
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)
@@ -3110,14 +3125,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
@@ -3148,23 +3163,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)
@@ -3175,22 +3191,21 @@
# 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_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_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()
@@ -3220,7 +3235,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
@@ -2,9 +2,15 @@
from rpython.rtyper.lltypesystem import lltype, llmemory
from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
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 rpython.rlib.rawrefcount import (REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT,
+ REFCNT_MASK)
+from pypy.module.cpyext.api import (PyObject, PyTypeObject, PyTypeObjectPtr,
+ PyObjectFields, cpython_struct)
+from pypy.module.cpyext.complexobject import PyComplexObject
+from rpython.rtyper.lltypesystem import rffi
+from pypy.module.cpyext.typeobjectdefs import visitproc, traverseproc
+from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.tool import rffi_platform
PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR
PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR
@@ -14,6 +20,17 @@
('prev', lltype.Ptr(S)),
('next', lltype.Ptr(S))))
+T = lltype.Ptr(lltype.ForwardReference())
+T.TO.become(lltype.Struct('test',
+ ('base', PyObject.TO),
+ ('next', T),
+ ('prev', T),
+ ('value', lltype.Signed)))
+
+TRAVERSE_FUNCTYPE = rffi.CCallback([PyObject, visitproc, rffi.VOIDP],
+ rffi.INT_real)
+t1 = lltype.malloc(PyTypeObject, flavor='raw', immortal=True)
+
class TestRawRefCount(BaseDirectGCTest):
GCClass = IncrementalMiniMarkGC
@@ -56,21 +73,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
@@ -81,19 +99,53 @@
return p1
return p1, p1ref, r1, r1addr, check_alive
+ def _rawrefcount_cycle_obj(self):
+
+ def test_tp_traverse(obj, visit, args):
+ test = rffi.cast(T, obj)
+ vret = 0
+ if llmemory.cast_ptr_to_adr(test.next).ptr is not None:
+ next = rffi.cast(PyObject, test.next)
+ vret = visit(next, args)
+ if vret != 0:
+ return vret
+ if llmemory.cast_ptr_to_adr(test.prev).ptr is not None:
+ next = rffi.cast(PyObject, test.prev)
+ vret = visit(next, args)
+ if vret != 0:
+ return vret
+ return vret
+
+ func_ptr = llhelper(TRAVERSE_FUNCTYPE, test_tp_traverse)
+ rffi_func_ptr = rffi.cast(traverseproc, func_ptr)
+ t1.c_tp_traverse = rffi_func_ptr
+
+ r1 = lltype.malloc(T.TO, flavor='raw', immortal=True)
+ r1.base.c_ob_pypy_link = 0
+ r1.base.c_ob_type = t1
+ r1.base.c_ob_refcnt = 1
+ return r1
+
+ def _rawrefcount_buffer_obj(self, obj):
+ from rpython.rlib.rawrefcount import REFCNT_CLR_MASK, REFCNT_CLR_PURPLE
+ rc = obj.base.c_ob_refcnt
+ obj.base.c_ob_refcnt = rc & ~REFCNT_CLR_MASK | REFCNT_CLR_PURPLE
+ objaddr = llmemory.cast_ptr_to_adr(obj)
+ self.gc.rawrefcount_buffer_pyobj(objaddr)
+
def test_rawrefcount_objects_basic(self, old=False):
p1, p1ref, r1, r1addr, check_alive = (
self._rawrefcount_pair(42, is_light=True, create_old=old))
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 +158,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 +181,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 +199,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 +216,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 +249,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 +266,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 +284,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 +304,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 +329,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 +341,146 @@
check_alive(0)
self._collect(major=True)
check_alive(0)
+
+ # def test_cycle_self_reference_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r1.next = r1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+ #
+ # def test_cycle_self_reference_not_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r1.base.c_ob_refcnt += 1
+ # r1.next = r1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 2
+ #
+ # def test_simple_cycle_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r2.next = r1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+ #
+ # def test_simple_cycle_not_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r2.next = r1
+ # r2.base.c_ob_refcnt += 1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 1
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 2
+ #
+ # def test_complex_cycle_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r3 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r1.prev = r2
+ # r2.base.c_ob_refcnt += 1
+ # r2.next = r3
+ # r3.prev = r1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r3.base.c_ob_refcnt & REFCNT_MASK == 0
+ #
+ # def test_complex_cycle_not_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r3 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r1.prev = r2
+ # r2.base.c_ob_refcnt += 1
+ # r2.next = r3
+ # r3.prev = r1
+ # r3.base.c_ob_refcnt += 1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 1
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 2
+ # assert r3.base.c_ob_refcnt & REFCNT_MASK == 2
+ #
+ # def test_cycle_2_buffered_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r2.prev = r1
+ # self._rawrefcount_buffer_obj(r1)
+ # self._rawrefcount_buffer_obj(r2)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+ #
+ # def test_cycle_2_buffered_not_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r2.prev = r1
+ # r1.base.c_ob_refcnt += 1
+ # self._rawrefcount_buffer_obj(r1)
+ # self._rawrefcount_buffer_obj(r2)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 2
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 1
+ #
+ # def test_multiple_cycles_partial_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r3 = self._rawrefcount_cycle_obj()
+ # r4 = self._rawrefcount_cycle_obj()
+ # r5 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r2.next = r3
+ # r3.next = r1
+ # r2.prev = r5
+ # r5.next = r4
+ # r4.next = r5
+ # r5.base.c_ob_refcnt += 1
+ # r4.base.c_ob_refcnt += 1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r3.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r4.base.c_ob_refcnt & REFCNT_MASK == 2
+ # assert r5.base.c_ob_refcnt & REFCNT_MASK == 1
+ #
+ # def test_multiple_cycles_all_free(self):
+ # self.gc.rawrefcount_init(lambda: self.trigger.append(1))
+ # r1 = self._rawrefcount_cycle_obj()
+ # r2 = self._rawrefcount_cycle_obj()
+ # r3 = self._rawrefcount_cycle_obj()
+ # r4 = self._rawrefcount_cycle_obj()
+ # r5 = self._rawrefcount_cycle_obj()
+ # r1.next = r2
+ # r2.next = r3
+ # r3.next = r1
+ # r2.prev = r5
+ # r5.next = r4
+ # r4.next = r5
+ # r5.base.c_ob_refcnt += 1
+ # self._rawrefcount_buffer_obj(r1)
+ # self.gc.rrc_collect_cycles()
+ # assert r1.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r2.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r3.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r4.base.c_ob_refcnt & REFCNT_MASK == 0
+ # assert r5.base.c_ob_refcnt & REFCNT_MASK == 0
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
@@ -489,6 +489,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)
@@ -1339,6 +1343,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
@@ -4,18 +4,49 @@
# This is meant for pypy's cpyext module, but is a generally
# useful interface over our GC. XXX "pypy" should be removed here
#
-import sys, weakref, py
+import sys, weakref, py, math
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
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
-REFCNT_FROM_PYPY = sys.maxint // 4 + 1
-REFCNT_FROM_PYPY_LIGHT = REFCNT_FROM_PYPY + (sys.maxint // 2 + 1)
+MAX_BIT = int(math.log(sys.maxint, 2))
+
+# Flags
+REFCNT_FROM_PYPY = 1 << MAX_BIT - 2 # Reference from a pypy object
+REFCNT_FROM_PYPY_LIGHT = (1 << MAX_BIT - 1) + REFCNT_FROM_PYPY # Light reference from a pypy object
+REFCNT_CYCLE_BUFFERED = 1 << MAX_BIT - 3 # Object in roots buffer (for potential cycles)
+REFCNT_IN_WAVEFRONT = 1 << MAX_BIT - 4 # Object in any wavefront
+
+# Offsets and sizes
+REFCNT_CLR_OFFS = MAX_BIT - 7
+REFCNT_CRC_OFFS = REFCNT_CLR_OFFS / 2
+REFCNT_BITS = REFCNT_CRC_OFFS - 1
+
+# Concurrent cycle collection colors
+REFCNT_CLR_BLACK = 0 << REFCNT_CLR_OFFS # In use or free (default)
+REFCNT_CLR_GRAY = 1 << REFCNT_CLR_OFFS # Possible member of cycle
+REFCNT_CLR_YELLOW = 2 << REFCNT_CLR_OFFS # Member of garbage cycle
+REFCNT_CLR_PURPLE = 3 << REFCNT_CLR_OFFS # Possible root of cycle
+REFCNT_CLR_GREEN = 4 << REFCNT_CLR_OFFS # Acyclic
+REFCNT_CLR_ORANGE = 5 << REFCNT_CLR_OFFS # In orange wavefront (might change to YELLOW + IN_WAVEFRONT + phase = 3)
+REFCNT_CLR_MASK = 7 << REFCNT_CLR_OFFS
+
+# Cyclic reference count with overflow bit
+REFCNT_CRC_OVERFLOW = 1 << REFCNT_CRC_OFFS + REFCNT_BITS
+REFCNT_CRC_MASK = (1 << REFCNT_CRC_OFFS + REFCNT_BITS + 1) - 1
+REFCNT_CRC = 1 < REFCNT_CRC_OFFS
+
+# True reference count with overflow bit
+REFCNT_OVERFLOW = 1 << REFCNT_BITS
+REFCNT_MASK = (1 << REFCNT_BITS + 1) - 1
+
RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
+W_MARKER_DEALLOCATING = W_Root()
def _build_pypy_link(p):
@@ -23,6 +54,47 @@
_adr2pypy.append(p)
return res
+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
+
+_refcount_overflow = dict()
+
+def overflow_new(obj):
+ _refcount_overflow[objectmodel.current_object_addr_as_int(obj)] = 0
+
+def overflow_add(obj):
+ _refcount_overflow[objectmodel.current_object_addr_as_int(obj)] += 1
+
+def overflow_sub(obj):
+ addr = objectmodel.current_object_addr_as_int(obj)
+ c = _refcount_overflow[addr]
+ if c > 0:
+ _refcount_overflow[addr] = c - 1
+ return False
+ else:
+ _refcount_overflow.pop(addr)
+ return True
+
+def overflow_get(obj):
+ return _refcount_overflow[objectmodel.current_object_addr_as_int(obj)]
+
+# TODO: _cyclic_refcount_overflow = dict()
@not_rpython
def init(dealloc_trigger_callback=None):
@@ -72,6 +144,10 @@
ob.c_ob_pypy_link = _build_pypy_link(marker)
@not_rpython
+def buffer_pyobj(ob):
+ pass # TODO: implement?
+
+ at not_rpython
def from_obj(OB_PTR_TYPE, p):
ob = _pypy2ob.get(p)
if ob is None:
@@ -122,7 +198,8 @@
wr_p_list = []
new_p_list = []
for ob in reversed(_p_list):
- if ob.c_ob_refcnt not in (REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT):
+ if ob.c_ob_refcnt & REFCNT_MASK > 0 \
+ or ob.c_ob_refcnt & REFCNT_FROM_PYPY == 0:
new_p_list.append(ob)
else:
p = detach(ob, wr_p_list)
@@ -155,7 +232,8 @@
if ob.c_ob_refcnt >= REFCNT_FROM_PYPY_LIGHT:
ob.c_ob_refcnt -= REFCNT_FROM_PYPY_LIGHT
ob.c_ob_pypy_link = 0
- if ob.c_ob_refcnt == 0:
+ if ob.c_ob_refcnt & REFCNT_MASK == 0 \
+ and ob.c_ob_refcnt < REFCNT_FROM_PYPY:
lltype.free(ob, flavor='raw',
track_allocation=track_allocation)
else:
@@ -163,8 +241,9 @@
assert ob.c_ob_refcnt < int(REFCNT_FROM_PYPY_LIGHT * 0.99)
ob.c_ob_refcnt -= REFCNT_FROM_PYPY
ob.c_ob_pypy_link = 0
- if ob.c_ob_refcnt == 0:
- ob.c_ob_refcnt = 1
+ if ob.c_ob_refcnt & REFCNT_MASK == 0 \
+ and ob.c_ob_refcnt < REFCNT_FROM_PYPY:
+ ob.c_ob_refcnt += 1
_d_list.append(ob)
return None
@@ -252,6 +331,17 @@
func_boehm_eci)
hop.genop('direct_call', [c_func])
+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
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -969,6 +969,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
@@ -494,6 +494,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):
More information about the pypy-commit
mailing list