Rev 172: Include an appropriate _MemObjectProxy.tp_traverse function. in http://bazaar.launchpad.net/~meliae-dev/meliae/trunk

John Arbash Meinel john at arbash-meinel.com
Thu Jul 29 22:29:38 BST 2010


At http://bazaar.launchpad.net/~meliae-dev/meliae/trunk

------------------------------------------------------------
revno: 172
revision-id: john at arbash-meinel.com-20100729212917-lc6ejysiyr3qvnq3
parent: john at arbash-meinel.com-20100729162825-p8n20h3zq758gjiz
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: trunk
timestamp: Thu 2010-07-29 16:29:17 -0500
message:
  Include an appropriate _MemObjectProxy.tp_traverse function.
  
  In the case that we have started managing the lifetime of the associated
  _MemObject struct, then we also start managing the lifetime of all the
  referenced PyObject * entries. So we need a tp_traverse that can get to them.
  
  Mostly I'm doing it here so that I can get it right before doing the big work
  on MemObjectCollection.
-------------- next part --------------
=== modified file 'meliae/_loader.pyx'
--- a/meliae/_loader.pyx	2010-07-29 16:28:25 +0000
+++ b/meliae/_loader.pyx	2010-07-29 21:29:17 +0000
@@ -22,6 +22,13 @@
     void *PyMem_Malloc(size_t)
     void PyMem_Free(void *)
 
+    ctypedef int (*visitproc)(PyObject *, void *)
+    ctypedef int (*traverseproc)(PyObject *, visitproc, void *)
+    ctypedef struct PyTypeObject:
+        # hashfunc tp_hash
+        # richcmpfunc tp_richcompare
+        traverseproc tp_traverse
+
     long PyObject_Hash(PyObject *) except -1
 
     object PyList_New(Py_ssize_t)
@@ -183,7 +190,7 @@
     RefList *parent_list
     unsigned long total_size
     # This is an uncounted ref to a _MemObjectProxy. _MemObjectProxy also has a
-    # refreence to this object, so when it disappears it can set the reference
+    # reference to this object, so when it disappears it can set the reference
     # to NULL.
     PyObject *proxy
 
@@ -1047,3 +1054,75 @@
             return next_proxy
         # if we got this far, then we don't have anything left:
         raise StopIteration()
+
+
+cdef int RefList_traverse(RefList *self, visitproc visit, void *arg):
+    """Equivalent of tp_traverse for a RefList.
+
+    RefList isn't a fully fledged python object/type, but since it can hold
+    counted references to python objects, we want the containing objects to be
+    able to tp_traverse to them.
+    """
+    cdef int ret
+    cdef long i
+
+    ret = 0
+    if self == NULL:
+        return ret
+    for i from 0 <= i < self.size:
+        ret = visit(self.refs[i], arg)
+        if ret:
+            return ret
+    return ret
+
+
+cdef int _MemObject_traverse(_MemObject *self, visitproc visit, void *arg):
+    """Equivalent idea of tp_traverse for _MemObject.
+
+    _MemObject isn't a fully fledged python object, but it can refer to python
+    objects. So we create a traverse function for it, so that gc and Meliae
+    itself can find the referenced objects.
+    """
+    cdef int ret
+
+    ret = 0
+    if self == NULL:
+        return ret
+    if ret == 0 and self.address != NULL:
+        ret = visit(self.address, arg)
+    if ret == 0 and self.type_str != NULL:
+        ret = visit(self.type_str, arg)
+    if ret == 0:
+        # RefList_traverse handles the NULL case
+        ret = RefList_traverse(self.child_list, visit, arg)
+    if ret == 0 and self.value != NULL:
+        ret = visit(self.value, arg)
+    if ret == 0:
+        ret = RefList_traverse(self.parent_list, visit, arg)
+    # Note: we *don't* incref the proxy because we know it links back to us. So
+    #       we don't tp_traverse to it, because we don't want gc thinking it
+    #       has enough references to destroy the object.
+    return ret
+
+
+cdef int _MemObjectProxy_traverse(_MemObjectProxy self, visitproc visit,
+                                  void *arg) except -1:
+    """Implement a correct tp_traverse because we use hidden members.
+    
+    Cython/Pyrex implement a tp_traverse, but it only handles 'object' members.
+    We use some private pointers to manage things, so we need a custom
+    tp_traverse to let everyone know about it.
+    """
+    # The specific detail is that _MemObjectProxy can sometimes control the
+    # reference to a _MemObject (if it was removed from a collection while the
+    # proxy was still alive). And that _MemObject structure can hold onto
+    # references to other real python objects.
+    cdef int ret
+
+    ret = 0
+    ret = visit(<PyObject *>self.collection, arg)
+    if ret == 0 and self._managed_obj != NULL:
+        ret = _MemObject_traverse(self._managed_obj, visit, arg)
+    return ret
+
+(<PyTypeObject*>_MemObjectProxy).tp_traverse = <traverseproc>_MemObjectProxy_traverse

=== modified file 'meliae/tests/test__loader.py'
--- a/meliae/tests/test__loader.py	2010-07-29 16:28:25 +0000
+++ b/meliae/tests/test__loader.py	2010-07-29 21:29:17 +0000
@@ -536,6 +536,19 @@
         # 8: PyObject *proxy
         self.assertSizeOf(5+8, mop, has_gc=True)
 
+    def test_traverse(self):
+        # When a Proxied object is removed from its Collection, it becomes
+        # owned by the Proxy itself, and should be returned from tp_traverse
+        mop = self.moc[0]
+        referenced = _scanner.get_referents(mop)
+        # At this point, moc still controls the _MemObject
+        self.assertEqual([self.moc], referenced)
+        # But now, it references everything else, too
+        del self.moc[0]
+        referenced = _scanner.get_referents(mop)
+        self.assertEqual([self.moc, mop.address, mop.type_str, mop.value],
+                         referenced)
+
 
 class Test_MemObjectProxyIterRecursiveRefs(tests.TestCase):
 



More information about the bazaar-commits mailing list