[pypy-commit] pypy cpyext-gc-support-2: copy some code, rekill parts that I definitely want killed
arigo
pypy.commits at gmail.com
Tue Jan 19 09:30:36 EST 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: cpyext-gc-support-2
Changeset: r81855:e02a927b7f7a
Date: 2016-01-19 15:29 +0100
http://bitbucket.org/pypy/pypy/changeset/e02a927b7f7a/
Log: copy some code, rekill parts that I definitely want killed
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
@@ -509,14 +509,16 @@
return {"PyObject*": PyObject, "PyTypeObject*": PyTypeObjectPtr,
"PyDateTime_CAPI*": lltype.Ptr(PyDateTime_CAPI)}[ctype]
+# Note: as a special case, "PyObject" is the pointer type in RPython,
+# corresponding to "PyObject *" in C. We do that only for PyObject.
+# For example, "PyTypeObject" is the struct type even in RPython.
PyTypeObject = lltype.ForwardReference()
PyTypeObjectPtr = lltype.Ptr(PyTypeObject)
-# It is important that these PyObjects are allocated in a raw fashion
-# Thus we cannot save a forward pointer to the wrapped object
-# So we need a forward and backward mapping in our State instance
PyObjectStruct = lltype.ForwardReference()
PyObject = lltype.Ptr(PyObjectStruct)
-PyObjectFields = (("ob_refcnt", lltype.Signed), ("ob_type", PyTypeObjectPtr))
+PyObjectFields = (("ob_refcnt", lltype.Signed),
+ ("ob_pypy_link", lltype.Signed),
+ ("ob_type", PyTypeObjectPtr))
PyVarObjectFields = PyObjectFields + (("ob_size", Py_ssize_t), )
cpython_struct('PyObject', PyObjectFields, PyObjectStruct)
PyVarObjectStruct = cpython_struct("PyVarObject", PyVarObjectFields)
@@ -827,6 +829,18 @@
space.fromcache(State).install_dll(eci)
+ def dealloc_trigger():
+ print 'dealloc_trigger...'
+ while True:
+ ob = rawrefcount.next_dead(PyObject)
+ if not ob:
+ break
+ print ob
+ _Py_Dealloc(space, ob)
+ print 'dealloc_trigger DONE'
+ return "RETRY"
+ rawrefcount.init(dealloc_trigger)
+
# populate static data
for name, (typ, expr) in GLOBALS.iteritems():
from pypy.module import cpyext
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
@@ -134,107 +134,6 @@
#________________________________________________________
# refcounted object support
-class RefcountState:
- def __init__(self, space):
- self.space = space
- self.py_objects_w2r = {} # { w_obj -> raw PyObject }
- self.py_objects_r2w = {} # { addr of raw PyObject -> w_obj }
-
- self.lifeline_dict = RWeakKeyDictionary(W_Root, PyOLifeline)
-
- self.borrow_mapping = {None: {}}
- # { w_container -> { w_containee -> None } }
- # the None entry manages references borrowed during a call to
- # generic_cpy_call()
-
- # For tests
- self.non_heaptypes_w = []
-
- def _cleanup_(self):
- assert self.borrow_mapping == {None: {}}
- self.py_objects_r2w.clear() # is not valid anymore after translation
-
- def init_r2w_from_w2r(self):
- """Rebuilds the dict py_objects_r2w on startup"""
- for w_obj, obj in self.py_objects_w2r.items():
- ptr = rffi.cast(ADDR, obj)
- self.py_objects_r2w[ptr] = w_obj
-
- def print_refcounts(self):
- print "REFCOUNTS"
- for w_obj, obj in self.py_objects_w2r.items():
- print "%r: %i" % (w_obj, obj.c_ob_refcnt)
-
- def get_from_lifeline(self, w_obj):
- lifeline = self.lifeline_dict.get(w_obj)
- if lifeline is not None: # make old PyObject ready for use in C code
- py_obj = lifeline.pyo
- assert py_obj.c_ob_refcnt == 0
- return py_obj
- else:
- return lltype.nullptr(PyObject.TO)
-
- def set_lifeline(self, w_obj, py_obj):
- self.lifeline_dict.set(w_obj,
- PyOLifeline(self.space, py_obj))
-
- def make_borrowed(self, w_container, w_borrowed):
- """
- Create a borrowed reference, which will live as long as the container
- has a living reference (as a PyObject!)
- """
- ref = make_ref(self.space, w_borrowed)
- obj_ptr = rffi.cast(ADDR, ref)
-
- borrowees = self.borrow_mapping.setdefault(w_container, {})
- if w_borrowed in borrowees:
- Py_DecRef(self.space, w_borrowed) # cancel incref from make_ref()
- else:
- borrowees[w_borrowed] = None
-
- return ref
-
- def reset_borrowed_references(self):
- "Used in tests"
- for w_container, w_borrowed in self.borrow_mapping.items():
- Py_DecRef(self.space, w_borrowed)
- self.borrow_mapping = {None: {}}
-
- def delete_borrower(self, w_obj):
- """
- Called when a potential container for borrowed references has lost its
- last reference. Removes the borrowed references it contains.
- """
- if w_obj in self.borrow_mapping: # move to lifeline __del__
- for w_containee in self.borrow_mapping[w_obj]:
- self.forget_borrowee(w_containee)
- del self.borrow_mapping[w_obj]
-
- def swap_borrow_container(self, container):
- """switch the current default contained with the given one."""
- if container is None:
- old_container = self.borrow_mapping[None]
- self.borrow_mapping[None] = {}
- return old_container
- else:
- old_container = self.borrow_mapping[None]
- self.borrow_mapping[None] = container
- for w_containee in old_container:
- self.forget_borrowee(w_containee)
-
- def forget_borrowee(self, w_obj):
- "De-register an object from the list of borrowed references"
- ref = self.py_objects_w2r.get(w_obj, lltype.nullptr(PyObject.TO))
- if not ref:
- if DEBUG_REFCOUNT:
- print >>sys.stderr, "Borrowed object is already gone!"
- return
-
- Py_DecRef(self.space, ref)
-
-class InvalidPointerException(Exception):
- pass
-
DEBUG_REFCOUNT = False
def debug_refcount(*args, **kwargs):
@@ -382,68 +281,6 @@
# "'s type which is", rffi.charp2str(pto.c_tp_name)
generic_cpy_call_dont_decref(space, pto.c_tp_dealloc, obj)
-#___________________________________________________________
-# Support for "lifelines"
-#
-# Object structure must stay alive even when not referenced
-# by any C code.
-
-class PyOLifeline(object):
- def __init__(self, space, pyo):
- self.pyo = pyo
- self.space = space
-
- def __del__(self):
- if self.pyo:
- assert self.pyo.c_ob_refcnt == 0
- _Py_Dealloc(self.space, self.pyo)
- self.pyo = lltype.nullptr(PyObject.TO)
- # XXX handle borrowed objects here
-
-#___________________________________________________________
-# Support for borrowed references
-
-def make_borrowed_ref(space, w_container, w_borrowed):
- """
- Create a borrowed reference, which will live as long as the container
- has a living reference (as a PyObject!)
- """
- if w_borrowed is None:
- return lltype.nullptr(PyObject.TO)
-
- state = space.fromcache(RefcountState)
- return state.make_borrowed(w_container, w_borrowed)
-
-class Reference:
- def __init__(self, pyobj):
- assert not isinstance(pyobj, W_Root)
- self.pyobj = pyobj
-
- def get_ref(self, space):
- return self.pyobj
-
- def get_wrapped(self, space):
- return from_ref(space, self.pyobj)
-
-class BorrowPair(Reference):
- """
- Delays the creation of a borrowed reference.
- """
- def __init__(self, w_container, w_borrowed):
- self.w_container = w_container
- self.w_borrowed = w_borrowed
-
- def get_ref(self, space):
- return make_borrowed_ref(space, self.w_container, self.w_borrowed)
-
- def get_wrapped(self, space):
- return self.w_borrowed
-
-def borrow_from(container, borrowed):
- return BorrowPair(container, borrowed)
-
-#___________________________________________________________
-
@cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
def _Py_HashPointer(space, ptr):
return rffi.cast(lltype.Signed, ptr)
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
@@ -1,8 +1,11 @@
from rpython.rlib.objectmodel import we_are_translated
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.error import OperationError
+from pypy.interpreter.executioncontext import AsyncAction
from rpython.rtyper.lltypesystem import lltype
+from rpython.rtyper.annlowlevel import llhelper
from rpython.rlib.rdynload import DLLHANDLE
+from rpython.rlib import rawrefcount
import sys
class State:
@@ -11,6 +14,8 @@
self.reset()
self.programname = lltype.nullptr(rffi.CCHARP.TO)
self.version = lltype.nullptr(rffi.CCHARP.TO)
+ pyobj_dealloc_action = PyObjDeallocAction(space)
+ self.dealloc_trigger = lambda: pyobj_dealloc_action.fire()
def reset(self):
from pypy.module.cpyext.modsupport import PyMethodDef
@@ -74,13 +79,15 @@
"This function is called when the program really starts"
from pypy.module.cpyext.typeobject import setup_new_method_def
- from pypy.module.cpyext.pyobject import RefcountState
from pypy.module.cpyext.api import INIT_FUNCTIONS
+ from pypy.module.cpyext.api import init_static_data_translated
+
+ if we_are_translated():
+ rawrefcount.init(llhelper(rawrefcount.RAWREFCOUNT_DEALLOC_TRIGGER,
+ self.dealloc_trigger))
+ init_static_data_translated(space)
setup_new_method_def(space)
- if we_are_translated():
- refcountstate = space.fromcache(RefcountState)
- refcountstate.init_r2w_from_w2r()
for func in INIT_FUNCTIONS:
func(space)
@@ -133,3 +140,17 @@
w_dict = w_mod.getdict(space)
w_copy = space.call_method(w_dict, 'copy')
self.extensions[path] = w_copy
+
+
+class PyObjDeallocAction(AsyncAction):
+ """An action that invokes _Py_Dealloc() on the dying PyObjects.
+ """
+
+ def perform(self, executioncontext, frame):
+ from pypy.module.cpyext.pyobject import PyObject, _Py_Dealloc
+
+ while True:
+ py_obj = rawrefcount.next_dead(PyObject)
+ if not py_obj:
+ break
+ _Py_Dealloc(self.space, py_obj)
More information about the pypy-commit
mailing list