[pypy-svn] r74554 - in pypy/trunk/pypy/module/cpyext: . test

afa at codespeak.net afa at codespeak.net
Wed May 19 01:55:14 CEST 2010


Author: afa
Date: Wed May 19 01:55:11 2010
New Revision: 74554

Modified:
   pypy/trunk/pypy/module/cpyext/api.py
   pypy/trunk/pypy/module/cpyext/modsupport.py
   pypy/trunk/pypy/module/cpyext/pyobject.py
   pypy/trunk/pypy/module/cpyext/state.py
   pypy/trunk/pypy/module/cpyext/test/test_cpyext.py
Log:
Add a new way to borrow objects:
borrow_from(None, w_obj) will keep the object alive until the end of the C function call
(managed by generic_cpy_call()).

>From what I understand, this is very similar to JNI "Local References".

Use this whenever there is no container managed by user code:
PyErr_Occurred(), PySys_GetObject()


Modified: pypy/trunk/pypy/module/cpyext/api.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/api.py	(original)
+++ pypy/trunk/pypy/module/cpyext/api.py	Wed May 19 01:55:11 2010
@@ -811,6 +811,7 @@
 @specialize.memo()
 def make_generic_cpy_call(FT, decref_args, expect_null):
     from pypy.module.cpyext.pyobject import make_ref, from_ref, Py_DecRef
+    from pypy.module.cpyext.pyobject import RefcountState
     from pypy.module.cpyext.pyerrors import PyErr_Occurred
     unrolling_arg_types = unrolling_iterable(enumerate(FT.ARGS))
     RESULT_TYPE = FT.RESULT
@@ -856,8 +857,17 @@
                     boxed_args += (arg,)
             else:
                 boxed_args += (arg,)
-        result = call_external_function(func, *boxed_args)
+
         try:
+            # create a new container for borrowed references
+            state = space.fromcache(RefcountState)
+            old_container = state.swap_borrow_container(None)
+            try:
+                # Call the function
+                result = call_external_function(func, *boxed_args)
+            finally:
+                state.swap_borrow_container(old_container)
+
             if RESULT_TYPE is PyObject:
                 if result is None:
                     ret = result

Modified: pypy/trunk/pypy/module/cpyext/modsupport.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/modsupport.py	(original)
+++ pypy/trunk/pypy/module/cpyext/modsupport.py	Wed May 19 01:55:11 2010
@@ -58,8 +58,7 @@
     if doc:
         space.setattr(w_mod, space.wrap("__doc__"),
                       space.wrap(rffi.charp2str(doc)))
-    # we cannot borrow here
-    return w_mod
+    return borrow_from(None, w_mod)
 
 
 def convert_method_defs(space, dict_w, methods, w_type, w_self=None):

Modified: pypy/trunk/pypy/module/cpyext/pyobject.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/pyobject.py	(original)
+++ pypy/trunk/pypy/module/cpyext/pyobject.py	Wed May 19 01:55:11 2010
@@ -151,8 +151,14 @@
         self.space = space
         self.py_objects_w2r = {} # { w_obj -> raw PyObject }
         self.py_objects_r2w = {} # { addr of raw PyObject -> w_obj }
-        self.borrow_mapping = {} # { w_container -> { w_containee -> None } }
-        self.borrowed_objects = {} # { addr of containee -> None }
+
+        self.borrow_mapping = {None: {}}
+        # { w_container -> { w_containee -> None } }
+        # the None entry manages references borrowed during a call to
+        # generic_cpy_call()
+        self.borrowed_objects = {}
+        # { addr of containee -> None }
+
         # For tests
         self.non_heaptypes_w = []
 
@@ -173,12 +179,69 @@
         for w_obj, obj in self.py_objects_w2r.items():
             print "%r: %i" % (w_obj, obj.c_ob_refcnt)
 
+    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)
+        if obj_ptr not in self.borrowed_objects:
+            # borrowed_objects owns the reference
+            self.borrowed_objects[obj_ptr] = None
+        else:
+            Py_DecRef(self.space, ref) # already in borrowed list
+
+        borrowees = self.borrow_mapping.setdefault(w_container, {})
+        borrowees[w_borrowed] = None
+        return ref
+
     def reset_borrowed_references(self):
+        "Used in tests"
         while self.borrowed_objects:
             addr, _ = self.borrowed_objects.popitem()
             w_obj = self.py_objects_r2w[addr]
             Py_DecRef(self.space, w_obj)
-        self.borrow_mapping = {}
+        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:", \
+                      hex(containee)
+            return
+
+        containee_ptr = rffi.cast(ADDR, ref)
+        try:
+            del self.borrowed_objects[containee_ptr]
+        except KeyError:
+            pass
+        else:
+            Py_DecRef(self.space, ref)
 
 class InvalidPointerException(Exception):
     pass
@@ -293,7 +356,7 @@
                 _Py_Dealloc(space, obj)
             del state.py_objects_w2r[w_obj]
             # if the object was a container for borrowed references
-            delete_borrower(space, w_obj)
+            state.delete_borrower(w_obj)
     else:
         if not we_are_translated() and obj.c_ob_refcnt < 0:
             message = "Negative refcount for obj %s with type %s" % (
@@ -329,23 +392,11 @@
     Create a borrowed reference, which will live as long as the container
     has a living reference (as a PyObject!)
     """
-    ref = make_ref(space, w_borrowed)
-    if not ref:
-        return ref
+    if w_borrowed is None:
+        return lltype.nullptr(PyObject.TO)
 
-    # state.borrowed_objects owns the reference
     state = space.fromcache(RefcountState)
-    obj_ptr = rffi.cast(ADDR, ref)
-    if obj_ptr not in state.borrowed_objects:
-        state.borrowed_objects[obj_ptr] = None
-    else:
-        Py_DecRef(space, ref) # already in borrowed list
-
-    if w_container is None: # self-managed
-        return ref
-    borrowees = state.borrow_mapping.setdefault(w_container, {})
-    borrowees[w_borrowed] = None
-    return ref
+    return state.make_borrowed(w_container, w_borrowed)
 
 class BorrowPair:
     """
@@ -361,36 +412,6 @@
 def borrow_from(container, borrowed):
     return BorrowPair(container, borrowed)
 
-def forget_borrowee(space, w_obj):
-    "De-register an object from the list of borrowed references"
-    state = space.fromcache(RefcountState)
-    ref = state.py_objects_w2r.get(w_obj, lltype.nullptr(PyObject.TO))
-    if not ref:
-        if DEBUG_REFCOUNT:
-            print >>sys.stderr, "Borrowed object is already gone:", \
-                  hex(containee)
-        return
-
-    containee_ptr = rffi.cast(ADDR, ref)
-    try:
-        del state.borrowed_objects[containee_ptr]
-    except KeyError:
-        pass
-    else:
-        Py_DecRef(space, ref)
-
-def delete_borrower(space, w_obj):
-    """
-    Called when a potential container for borrowed references has lost its
-    last reference.  Removes the borrowed references it contains.
-    """
-    state = space.fromcache(RefcountState)
-    if w_obj in state.borrow_mapping: # move to lifeline __del__
-        for w_containee in state.borrow_mapping[w_obj]:
-            forget_borrowee(space, w_containee)
-        del state.borrow_mapping[w_obj]
-
-
 #___________________________________________________________
 
 @cpython_api([rffi.VOIDP_real], lltype.Signed, error=CANNOT_FAIL)

Modified: pypy/trunk/pypy/module/cpyext/state.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/state.py	(original)
+++ pypy/trunk/pypy/module/cpyext/state.py	Wed May 19 01:55:11 2010
@@ -29,17 +29,9 @@
         self.operror = operror
 
     def clear_exception(self):
-        """Clear the current exception state, and return the operror.
-        Also frees the borrowed reference returned by PyErr_Occurred()
-        """
+        """Clear the current exception state, and return the operror."""
         from pypy.module.cpyext.api import ADDR
-        # handling of borrowed objects, remove when we have
-        # a weakkeydict
         operror = self.operror
-        if operror is not None:
-            # we have no explicit container
-            from pypy.module.cpyext.pyobject import forget_borrowee
-            forget_borrowee(self.space, operror.w_type)
         self.operror = None
         return operror
 

Modified: pypy/trunk/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_cpyext.py	(original)
+++ pypy/trunk/pypy/module/cpyext/test/test_cpyext.py	Wed May 19 01:55:11 2010
@@ -249,14 +249,11 @@
 
     def unimport_module(self, name):
         """
-        Remove the named module from the space's sys.modules and discard the
-        reference (owned by "the test") to it.
+        Remove the named module from the space's sys.modules.
         """
         w_modules = self.space.sys.get('modules')
         w_name = self.space.wrap(name)
-        w_mod = self.space.getitem(w_modules, w_name)
         self.space.delitem(w_modules, w_name)
-        Py_DecRef(self.space, w_mod)
 
     def teardown_method(self, func):
         for name in self.imported_module_names:



More information about the Pypy-commit mailing list